diff --git a/sdk/src/utils/numbers.ts b/sdk/src/utils/numbers.ts index be6eeb2bfa..9c1928be77 100644 --- a/sdk/src/utils/numbers.ts +++ b/sdk/src/utils/numbers.ts @@ -105,7 +105,7 @@ export const trimZeroDecimals = (amount: string) => { export function bigintToNumber(value: bigint, decimals: number) { const negative = value < 0; if (negative) value *= -1n; - const precision = 10n ** BigInt(decimals); + const precision = expandDecimals(1, decimals); const int = value / precision; const frac = value % precision; diff --git a/src/components/BlockField/BlockField.tsx b/src/components/BlockField/BlockField.tsx new file mode 100644 index 0000000000..12336e50e1 --- /dev/null +++ b/src/components/BlockField/BlockField.tsx @@ -0,0 +1,108 @@ +import cx from "classnames"; +import { + ReactNode, + useCallback, + useRef, + type MouseEvent, + type PointerEvent, + type Ref, + type MutableRefObject, +} from "react"; + +type Props = { + label: ReactNode; + content: ReactNode; + className?: string; + labelClassName?: string; + contentClassName?: string; + containerRef?: Ref; + forwardClickToSelector?: boolean; +}; + +export function BlockField({ + label, + content, + className, + labelClassName, + contentClassName, + containerRef, + forwardClickToSelector, +}: Props) { + const localRef = useRef(null); + const wasOpenOnPointerDown = useRef(false); + + const setContainerRef = useCallback( + (node: HTMLDivElement | null) => { + localRef.current = node; + if (!containerRef) { + return; + } + if (typeof containerRef === "function") { + containerRef(node); + } else { + (containerRef as MutableRefObject).current = node; + } + }, + [containerRef] + ); + + const handlePointerDown = useCallback( + (event: PointerEvent) => { + if (!forwardClickToSelector) { + return; + } + const button = localRef.current?.querySelector(".SelectorBase-button"); + const target = event.target as Node | null; + + if (!button || (target && button.contains(target))) { + wasOpenOnPointerDown.current = false; + return; + } + + const rootState = button.closest("[data-headlessui-state]")?.getAttribute("data-headlessui-state") ?? ""; + const buttonState = button.getAttribute("data-headlessui-state") ?? ""; + wasOpenOnPointerDown.current = + button.getAttribute("aria-expanded") === "true" || rootState.includes("open") || buttonState.includes("open"); + }, + [forwardClickToSelector] + ); + + const handleClick = useCallback( + (event: MouseEvent) => { + if (!forwardClickToSelector) { + return; + } + const button = localRef.current?.querySelector(".SelectorBase-button"); + const target = event.target as Node | null; + + if (!button || button.classList.contains("SelectorBase-button-disabled") || (target && button.contains(target))) { + wasOpenOnPointerDown.current = false; + return; + } + + if (wasOpenOnPointerDown.current) { + wasOpenOnPointerDown.current = false; + return; + } + + button.click(); + wasOpenOnPointerDown.current = false; + }, + [forwardClickToSelector] + ); + + return ( +
+
{label}
+
{content}
+
+ ); +} diff --git a/src/components/CollateralSelector/CollateralSelector.tsx b/src/components/CollateralSelector/CollateralSelector.tsx index 441a51ab4e..833e7a845c 100644 --- a/src/components/CollateralSelector/CollateralSelector.tsx +++ b/src/components/CollateralSelector/CollateralSelector.tsx @@ -33,13 +33,22 @@ type Props = { options: TokenData[] | undefined; disabledOptions?: TokenData[]; onSelect: (tokenAddress: string) => void; + // eslint-disable-next-line react/no-unused-prop-types + popoverReferenceRef?: React.RefObject; }; export function CollateralSelector(props: Props) { const isMobile = useMedia(`(max-width: ${SELECTOR_BASE_MOBILE_THRESHOLD}px)`); return ( - + {isMobile ? : } ); diff --git a/src/components/ExitPriceRow/ExitPriceRow.tsx b/src/components/ExitPriceRow/ExitPriceRow.tsx index 6507be34b1..51156a32e3 100644 --- a/src/components/ExitPriceRow/ExitPriceRow.tsx +++ b/src/components/ExitPriceRow/ExitPriceRow.tsx @@ -34,7 +34,7 @@ export function ExitPriceRow({ diff --git a/src/components/ExpandableRow.tsx b/src/components/ExpandableRow.tsx index 0dcaf85553..105ef37555 100644 --- a/src/components/ExpandableRow.tsx +++ b/src/components/ExpandableRow.tsx @@ -66,6 +66,7 @@ interface Props { withToggleSwitch?: boolean; handleClassName?: string; chevronClassName?: string; + wrapped?: boolean; } export function ExpandableRow({ @@ -83,6 +84,7 @@ export function ExpandableRow({ withToggleSwitch = false, handleClassName, chevronClassName, + wrapped = false, }: Props) { const previousHasError = usePrevious(hasError); const contentRef = useRef(null); @@ -135,9 +137,18 @@ export function ExpandableRow({ ); return ( -
+
-
+
void; + marks: number[]; +}; + +function clampLeverage(value: number, min: number, max: number) { + const safeMin = min > 0 ? min : DEFAULT_LEVERAGE; + return Math.min(Math.max(value, safeMin), max); +} + +function formatLeverage(value: number) { + return parseFloat(value.toFixed(2)).toString(); +} + +export function LeverageField({ value, onChange, marks }: Props) { + const finalMarks = useMemo(() => (marks?.length ? marks : defaultMarks), [marks]); + const minMark = finalMarks[0] ?? DEFAULT_LEVERAGE; + const maxMark = finalMarks.at(-1) ?? DEFAULT_LEVERAGE; + + const [inputValue, setInputValue] = useState(() => { + if (value !== null) { + return formatLeverage(clampLeverage(value, minMark, maxMark)); + } + + return formatLeverage(minMark); + }); + + useEffect(() => { + if (value !== null) { + setInputValue(formatLeverage(clampLeverage(value, minMark, maxMark))); + } + }, [value, minMark, maxMark]); + + const parseAndClampValue = useCallback( + (rawValue: string) => { + const numericValue = parseFloat(rawValue); + + if (Number.isNaN(numericValue) || numericValue <= 0) { + return undefined; + } + + return clampLeverage(numericValue, minMark, maxMark); + }, + [maxMark, minMark] + ); + + const commitValue = useCallback( + (rawValue?: string) => { + const nextValue = rawValue ?? inputValue; + const parsed = parseAndClampValue(nextValue); + const fallback = clampLeverage(value ?? minMark, minMark, maxMark); + const finalValue = parsed ?? fallback; + const formatted = formatLeverage(finalValue); + + setInputValue(formatted); + onChange(finalValue); + }, + [inputValue, maxMark, minMark, onChange, value, parseAndClampValue] + ); + + const handleInputChange = useCallback( + (nextValue: string) => { + setInputValue(nextValue); + + const parsed = parseAndClampValue(nextValue); + if (parsed !== undefined) { + onChange(parsed); + } + }, + [parseAndClampValue, onChange] + ); + + return ( +
+ +
+ ); +} diff --git a/src/components/LeverageSlider/LeverageSlider.tsx b/src/components/LeverageSlider/LeverageSlider.tsx deleted file mode 100644 index af9940a2cb..0000000000 --- a/src/components/LeverageSlider/LeverageSlider.tsx +++ /dev/null @@ -1,187 +0,0 @@ -import cx from "classnames"; -import range from "lodash/range"; -import Slider, { Handle, SliderTooltip } from "rc-slider"; -import { forwardRef, useCallback, useEffect, useMemo } from "react"; - -import "rc-slider/assets/index.css"; -import "./LeverageSlider.scss"; - -const defaultMarks = [0.1, 25, 50]; -const DEFAULT_LEVERAGE_KEY = 20; - -type Props = { - isPositive?: boolean; - value?: number; - onChange: (value: number) => void; - marks: number[]; - className?: string; - isSlim?: boolean; -}; - -type HandleProps = { - value: number; - dragging: boolean; - index: number; - displayValue: number; -}; - -function getMarksWithLabel(marks: number[]) { - return marks.reduce( - (marks, value, index) => { - marks[index * 10] = `${value}x`; - return marks; - }, - {} as { [key: number]: string } - ); -} - -export function LeverageSlider(p: Props) { - const { onChange, value, marks } = p; - const finalMarks = marks ?? defaultMarks; - - const { keyValueMap, valueKeyMap } = useMemo(() => generateKeyValueMap(finalMarks), [finalMarks]); - - const firstValue = finalMarks[0]; - const firstKey = valueKeyMap[firstValue]; - const sliderKey = value === undefined ? firstKey : valueKeyMap[value] ?? sliderValueToSliderKey(keyValueMap, value); - - const max = (finalMarks.length - 1) * 10; - - const handleChange = useCallback( - (newKey: number) => { - const truncatedKey = Math.trunc(newKey ?? DEFAULT_LEVERAGE_KEY); - onChange(keyValueMap[truncatedKey] ?? keyValueMap[DEFAULT_LEVERAGE_KEY]); - }, - [onChange, keyValueMap] - ); - - const customHandle = useMemo(() => { - return (props: any) => ; - }, [value]); - - const leverageSliderMarks = useMemo(() => { - return Object.fromEntries( - Object.entries(getMarksWithLabel(finalMarks)) - .sort((a, b) => Number(a[0]) - Number(b[0])) - .map(([key, value]) => [ - key, - { - label: value, - }, - ]) - ); - }, [finalMarks]); - - return ( -
- -
- ); -} - -const LeverageSliderHandle = forwardRef(function LeverageSliderHandle( - { value, dragging, index, displayValue, ...restProps }, - ref -) { - useEffect(() => { - if (dragging) { - document.body.classList.add("dragging"); - } else { - document.body.classList.remove("dragging"); - } - }, [dragging]); - - return ( - - - - ); -}); - -function generateEquallySpacedArray(min: number, max: number, shouldIncludeMax?: boolean): number[] { - const step = (max - min) / 10; - let array = range(min, max, step).map((num) => parseFloat(num.toFixed(1))); - - if (shouldIncludeMax && array[array.length - 1] !== max) { - array.push(max); - } - - return array; -} - -function generateKeyValueMap(marks: number[]) { - const values = marks.slice(0, -1).flatMap((mark, index) => { - const shouldIncludeMax = index === marks.length - 2; - return generateEquallySpacedArray(mark, marks[index + 1], shouldIncludeMax); - }); - - const keyValueMap = Object.fromEntries(values.map((value, index) => [index, value])); - const valueKeyMap = Object.fromEntries(values.map((value, index) => [value, index])); - - return { keyValueMap, valueKeyMap }; -} - -function sliderValueToSliderKey(keyValueMap: { [key: number]: number }, value: number): number { - const sortedValues = Array.from({ - length: Object.keys(keyValueMap).length, - }).map((_, index) => keyValueMap[index]); - - let leftIndex = sortedValues.findIndex((val) => val >= value); - if (leftIndex !== -1) { - if (sortedValues[leftIndex] === value) { - return leftIndex; - } - leftIndex -= 1; - } else { - leftIndex = 0; - } - - const leftValue = sortedValues[leftIndex - 1]; - if (leftValue === undefined) { - return sortedValues[0]; - } - - let rightIndex = sortedValues.findLastIndex((val) => val <= value); - if (rightIndex !== -1) { - if (sortedValues[rightIndex] === value) { - return rightIndex; - } - rightIndex += 1; - } else { - rightIndex = sortedValues.length - 1; - } - - const rightValue = sortedValues[rightIndex]; - - if (rightValue === undefined) { - return sortedValues[sortedValues.length - 1]; - } - - return (value - leftValue) / (rightValue - leftValue) + leftIndex; -} diff --git a/src/components/Modal/Modal.css b/src/components/Modal/Modal.css index 558829c5cc..f590cd6204 100644 --- a/src/components/Modal/Modal.css +++ b/src/components/Modal/Modal.css @@ -55,6 +55,12 @@ align-items: center; } +.Modal-title-group { + display: flex; + align-items: center; + gap: 8px; +} + .Modal-title { text-align: left; font-size: var(--font-size-body-large); diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx index 5fe4a2ae56..5777d1e1da 100644 --- a/src/components/Modal/Modal.tsx +++ b/src/components/Modal/Modal.tsx @@ -3,8 +3,10 @@ import { AnimatePresence, Variants, motion } from "framer-motion"; import React, { PropsWithChildren, ReactNode, useCallback, useEffect, useMemo, useRef } from "react"; import { RemoveScroll } from "react-remove-scroll"; +import Button from "components/Button/Button"; import ErrorBoundary from "components/Errors/ErrorBoundary"; +import ChevronLeftIcon from "img/ic_chevron_left.svg?react"; import CloseIcon from "img/ic_close.svg?react"; import "./Modal.css"; @@ -32,6 +34,7 @@ export type ModalProps = PropsWithChildren<{ setIsVisible: (isVisible: boolean) => void; zIndex?: number; label?: React.ReactNode; + onBack?: () => void; headerContent?: React.ReactNode; footerContent?: ReactNode; onAfterOpen?: () => void; @@ -49,6 +52,7 @@ export default function Modal({ className, isVisible, label, + onBack, zIndex, children, headerContent, @@ -132,7 +136,14 @@ export default function Modal({ >
-
{label}
+
+ {onBack && ( + + )} +
{label}
+
setIsVisible(false)}>
diff --git a/src/components/NetworkFeeRow/NetworkFeeRow.tsx b/src/components/NetworkFeeRow/NetworkFeeRow.tsx index ab71681edc..bcfbc567ad 100644 --- a/src/components/NetworkFeeRow/NetworkFeeRow.tsx +++ b/src/components/NetworkFeeRow/NetworkFeeRow.tsx @@ -228,7 +228,7 @@ export function NetworkFeeRow({ executionFee, gasPaymentParams, isAdditionOrders label={ The maximum network fee paid to the network. This fee is a blockchain cost not specific to GMX, and it @@ -249,7 +249,7 @@ export function NetworkFeeRow({ executionFee, gasPaymentParams, isAdditionOrders label={ The maximum network fee paid to the network. This fee is a blockchain cost not specific to GMX, and it diff --git a/src/components/OrderEditor/OrderEditor.tsx b/src/components/OrderEditor/OrderEditor.tsx index d7b6249189..d0a1746649 100644 --- a/src/components/OrderEditor/OrderEditor.tsx +++ b/src/components/OrderEditor/OrderEditor.tsx @@ -85,26 +85,28 @@ import { formatUsdPrice, parseValue, } from "lib/numbers"; -import { getByKey } from "lib/objects"; import { useJsonRpcProvider } from "lib/rpc"; import { useHasOutdatedUi } from "lib/useHasOutdatedUi"; import { sendEditOrderEvent } from "lib/userAnalytics"; import useWallet from "lib/wallets/useWallet"; import { bigMath } from "sdk/utils/bigmath"; -import { BatchOrderTxnParams, buildUpdateOrderPayload } from "sdk/utils/orderTransactions"; +import { BatchOrderTxnParams, buildUpdateOrderPayload, getBatchTotalExecutionFee } from "sdk/utils/orderTransactions"; import { AcceptablePriceImpactInputRow } from "components/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow"; import Button from "components/Button/Button"; import BuyInputSection from "components/BuyInputSection/BuyInputSection"; +import { ExpandableRow } from "components/ExpandableRow"; import ExternalLink from "components/ExternalLink/ExternalLink"; import Modal from "components/Modal/Modal"; import StatsTooltipRow from "components/StatsTooltip/StatsTooltipRow"; import TooltipWithPortal from "components/Tooltip/TooltipWithPortal"; +import { TPSLInputRow } from "components/TPSLModal/TPSLInputRow"; import { ValueTransition } from "components/ValueTransition/ValueTransition"; import { AllowedSwapSlippageInputRow } from "../AllowedSwapSlippageInputRowImpl/AllowedSwapSlippageInputRowImpl"; import { SyntheticsInfoRow } from "../SyntheticsInfoRow"; import { ExpressTradingWarningCard } from "../TradeBox/ExpressTradingWarningCard"; +import { useOrderEditorTPSL } from "./hooks/useOrderEditorTPSL"; import "./OrderEditor.scss"; @@ -112,6 +114,7 @@ type Props = { order: OrderInfo; source: EditingOrderSource; onClose: () => void; + onBack?: () => void; }; export function OrderEditor(p: Props) { @@ -155,7 +158,7 @@ export function OrderEditor(p: Props) { const additionalTokenAmount = executionFee.feeTokenAmount - p.order.executionFee; return { - feeUsd: convertToUsd(additionalTokenAmount, executionFee.feeToken.decimals, feeTokenData?.prices.minPrice), + feeUsd: convertToUsd(additionalTokenAmount, executionFee.feeToken.decimals, feeTokenData?.prices.minPrice) ?? 0n, feeTokenAmount: additionalTokenAmount, feeToken: executionFee.feeToken, }; @@ -175,6 +178,7 @@ export function OrderEditor(p: Props) { const userReferralInfo = useUserReferralInfo(); const { uiFeeFactor } = useUiFeeFactorRequest(chainId); + const { savedAcceptablePriceImpactBuffer, isSetAcceptablePriceImpactEnabled } = useSettings(); const acceptablePrice = useSelector(selectOrderEditorAcceptablePrice); const acceptablePriceImpactBps = useSelector(selectOrderEditorAcceptablePriceImpactBps); @@ -199,6 +203,21 @@ export function OrderEditor(p: Props) { ? bigMath.abs(decreaseAmounts?.recommendedAcceptablePriceDeltaBps) : undefined; + const { + isLimitOrStopIncrease, + isTpSlEnabled, + setIsTpSlEnabled, + tpEntry, + slEntry, + tpSlPositionData, + tpSlCreatePayloads, + sidecarExecutionFee, + tpSlError, + tpSlHasError, + setTpPriceInputValue, + setSlPriceInputValue, + } = useOrderEditorTPSL(); + const priceImpactFeeBps = useSelector(selectOrderEditorPriceImpactFeeBps); const isMaxLeverageError = useMemo(() => { @@ -218,8 +237,6 @@ export function OrderEditor(p: Props) { return false; }, [p.order, sizeDeltaUsd, nextPositionValuesWithoutPnlForIncrease?.nextLeverage]); - const { savedAcceptablePriceImpactBuffer, isSetAcceptablePriceImpactEnabled } = useSettings(); - const detectAndSetAvailableMaxLeverage = useCallback(() => { const positionOrder = p.order as PositionOrderInfo; const marketInfo = positionOrder.marketInfo; @@ -348,7 +365,7 @@ export function OrderEditor(p: Props) { const positionOrder = p.order as PositionOrderInfo; return { - createOrderParams: [], + createOrderParams: tpSlCreatePayloads, updateOrderParams: [ buildUpdateOrderPayload({ chainId, @@ -379,6 +396,7 @@ export function OrderEditor(p: Props) { acceptablePrice, minOutputAmount, additionalExecutionFee?.feeTokenAmount, + tpSlCreatePayloads, ]); const { expressParams, expressParamsPromise } = useExpressOrdersParams({ @@ -388,28 +406,12 @@ export function OrderEditor(p: Props) { }); const networkFee = useMemo(() => { - if (!additionalExecutionFee) { + if (!batchParams || !tokensData) { return undefined; } - let feeToken = additionalExecutionFee?.feeToken; - let feeTokenAmount = additionalExecutionFee?.feeTokenAmount; - let feeUsd = additionalExecutionFee?.feeUsd; - - const gasPaymentToken = getByKey(tokensData, expressParams?.gasPaymentParams.gasPaymentTokenAddress); - - if (gasPaymentToken && expressParams?.gasPaymentParams.gasPaymentTokenAmount !== undefined) { - feeToken = gasPaymentToken; - feeTokenAmount = expressParams?.gasPaymentParams.gasPaymentTokenAmount; - feeUsd = convertToUsd(feeTokenAmount, feeToken.decimals, gasPaymentToken.prices.minPrice); - } - - return { - feeToken, - feeTokenAmount, - feeUsd, - }; - }, [additionalExecutionFee, expressParams, tokensData]); + return getBatchTotalExecutionFee({ batchParams, chainId, tokensData }); + }, [batchParams, chainId, tokensData]); const error = useMemo(() => { if (isSubmitting) { @@ -446,7 +448,13 @@ export function OrderEditor(p: Props) { return; } - return calcSelector(selectOrderEditorPositionOrderError); + const positionError = calcSelector(selectOrderEditorPositionOrderError); + + if (positionError) { + return positionError; + } + + return tpSlError; }, [ isSubmitting, p.order.orderType, @@ -459,8 +467,15 @@ export function OrderEditor(p: Props) { chainId, expressParams, tokensData, + tpSlError, ]); + useEffect(() => { + setIsTpSlEnabled(false); + setTpPriceInputValue(""); + setSlPriceInputValue(""); + }, [p.order.key, setIsTpSlEnabled, setSlPriceInputValue, setTpPriceInputValue]); + const onSubmit = useCallback(async () => { if (!batchParams || !signer || !tokensData || !marketsInfoData || !provider) { return; @@ -482,8 +497,10 @@ export function OrderEditor(p: Props) { isGmxAccount: srcChainId !== undefined, }); + const closeAfterSubmit = p.onBack ?? p.onClose; + if (expressParams?.subaccount) { - p.onClose(); + closeAfterSubmit(); setIsSubmitting(false); if (market) { sendEditOrderEvent({ order: p.order, source: p.source, marketInfo: market }); @@ -493,7 +510,7 @@ export function OrderEditor(p: Props) { txnPromise .then(() => { - p.onClose(); + closeAfterSubmit(); if (market) { sendEditOrderEvent({ order: p.order, source: p.source, marketInfo: market }); } @@ -660,6 +677,15 @@ export function OrderEditor(p: Props) { const sizeUsd = parseValue(sizeInputValue || "0", USD_DECIMALS)!; + const handleBack = useCallback(() => { + if (p.onBack) { + p.onBack(); + return; + } + + p.onClose(); + }, [p]); + return (
Edit {p.order.title}} + contentPadding={false} + onBack={p.onBack ? handleBack : undefined} > -
+
{!isSwapOrderType(p.order.orderType) && ( <> )}
+ {isLimitOrStopIncrease && tpSlPositionData && ( +
+ Take Profit / Stop Loss} + variant="iconStroke" + position="bottom" + content={ + + Create basic TP/SL orders that fully close your position. For advanced TP/SL setup, use the + positions list after opening a position. + + } + /> + } + hasError={tpSlHasError} + disableCollapseOnError + autoExpandOnError + errorMessage={There are issues in the TP/SL orders.} + onToggle={setIsTpSlEnabled} + withToggleSwitch + > +
+ { + setIsTpSlEnabled(true); + setTpPriceInputValue(value); + }} + positionData={tpSlPositionData} + priceError={tpEntry.price.error ?? undefined} + variant="compact" + /> + { + setIsTpSlEnabled(true); + setSlPriceInputValue(value); + }} + positionData={tpSlPositionData} + priceError={slEntry.price.error ?? undefined} + variant="compact" + defaultDisplayMode="usd" + /> +
+
+
+ )} -
+
{button} )} - {networkFee && ( + {networkFee && isTpSlEnabled && ( ( + content={ <> {t`Network Fee`}:
} @@ -816,18 +896,52 @@ export function OrderEditor(p: Props) { networkFee.feeToken.symbol, networkFee.feeToken.decimals, { - displayDecimals: 5, + displayDecimals: networkFee.feeToken.priceDecimals, isStable: networkFee.feeToken.isStable, } )} showDollar={false} /> + {additionalExecutionFee && ( + {t`Order update fee`}:
} + value={formatTokenAmountWithUsd( + additionalExecutionFee.feeTokenAmount * -1n, + additionalExecutionFee.feeUsd === undefined + ? undefined + : additionalExecutionFee.feeUsd * -1n, + additionalExecutionFee.feeToken.symbol, + additionalExecutionFee.feeToken.decimals, + { + displayDecimals: additionalExecutionFee.feeToken.priceDecimals, + isStable: additionalExecutionFee.feeToken.isStable, + } + )} + showDollar={false} + /> + )} + {sidecarExecutionFee && ( + {t`TP/SL fees`}:
} + value={formatTokenAmountWithUsd( + sidecarExecutionFee.feeTokenAmount * -1n, + sidecarExecutionFee.feeUsd === undefined ? undefined : sidecarExecutionFee.feeUsd * -1n, + sidecarExecutionFee.feeToken.symbol, + sidecarExecutionFee.feeToken.decimals, + { + displayDecimals: sidecarExecutionFee.feeToken.priceDecimals, + isStable: sidecarExecutionFee.feeToken.isStable, + } + )} + showDollar={false} + /> + )}
As network fees have increased, an additional network fee is needed.
- )} + } /> } /> diff --git a/src/components/OrderEditor/hooks/useOrderEditorTPSL.ts b/src/components/OrderEditor/hooks/useOrderEditorTPSL.ts new file mode 100644 index 0000000000..7283afe436 --- /dev/null +++ b/src/components/OrderEditor/hooks/useOrderEditorTPSL.ts @@ -0,0 +1,581 @@ +import { t } from "@lingui/macro"; +import { useCallback, useEffect, useMemo, useState } from "react"; + +import { USD_DECIMALS } from "config/factors"; +import { useMarketInfo } from "context/SyntheticsStateContext/hooks/marketHooks"; +import { + selectChainId, + selectGasLimits, + selectGasPrice, + selectIsAutoCancelTPSLEnabled, + selectMaxAutoCancelOrders, + selectPositionConstants, + selectTokensData, + selectUiFeeFactor, + selectUserReferralInfo, +} from "context/SyntheticsStateContext/selectors/globalSelectors"; +import { + selectOrderEditorExistingPosition, + selectOrderEditorIncreaseAmounts, + selectOrderEditorIndexToken, + selectOrderEditorNextPositionValuesForIncrease, + selectOrderEditorOrder, + selectOrderEditorPositionKey, + selectOrderEditorSizeDeltaUsd, + selectOrderEditorTriggerPrice, +} from "context/SyntheticsStateContext/selectors/orderEditorSelectors"; +import { makeSelectOrdersByPositionKey } from "context/SyntheticsStateContext/selectors/orderSelectors"; +import { selectIsSetAcceptablePriceImpactEnabled } from "context/SyntheticsStateContext/selectors/settingsSelectors"; +import { useSelector } from "context/SyntheticsStateContext/utils"; +import { + estimateExecuteDecreaseOrderGasLimit, + estimateOrderOraclePriceCount, + ExecutionFee, +} from "domain/synthetics/fees"; +import { + DecreasePositionSwapType, + OrderType, + PositionOrderInfo, + isLimitIncreaseOrderType, + isPositionOrder, + isStopIncreaseOrderType, +} from "domain/synthetics/orders"; +import { PositionInfo, PositionInfoLoaded } from "domain/synthetics/positions"; +import { EntryField, SidecarSlTpOrderEntry } from "domain/synthetics/sidecarOrders/types"; +import { + getDefaultEntryField, + handleEntryError, + MAX_PERCENTAGE, + PERCENTAGE_DECIMALS, +} from "domain/synthetics/sidecarOrders/utils"; +import type { NextPositionValues } from "domain/synthetics/trade/types"; +import { + buildTpSlCreatePayloads, + buildTpSlInputPositionData, + buildTpSlPositionInfo, + getTpSlDecreaseAmounts, +} from "domain/tpsl/sidecar"; +import { + calculateTotalSizeInTokens, + calculateTotalSizeUsd, + getCollateralDeltaAmount, + getCollateralDeltaUsd, +} from "domain/tpsl/utils"; +import { DecreasePositionAmounts } from "sdk/types/trade"; +import { getExecutionFee } from "sdk/utils/fees/executionFee"; + +export function useOrderEditorTPSL() { + const orderInfo = useSelector(selectOrderEditorOrder); + const order = orderInfo && isPositionOrder(orderInfo) ? orderInfo : undefined; + const existingPosition = useSelector(selectOrderEditorExistingPosition); + const sizeDeltaUsd = useSelector(selectOrderEditorSizeDeltaUsd); + const triggerPrice = useSelector(selectOrderEditorTriggerPrice); + const indexToken = useSelector(selectOrderEditorIndexToken); + const increaseAmounts = useSelector(selectOrderEditorIncreaseAmounts); + const nextPositionValuesForIncrease = useSelector(selectOrderEditorNextPositionValuesForIncrease); + const positionKey = useSelector(selectOrderEditorPositionKey); + const { minCollateralUsd, minPositionSizeUsd } = useSelector(selectPositionConstants); + const gasLimits = useSelector(selectGasLimits); + const gasPrice = useSelector(selectGasPrice); + const tokensData = useSelector(selectTokensData); + const chainId = useSelector(selectChainId)!; + const isAutoCancelTPSL = useSelector(selectIsAutoCancelTPSLEnabled); + const maxAutoCancelOrders = useSelector(selectMaxAutoCancelOrders); + const userReferralInfo = useSelector(selectUserReferralInfo); + const uiFeeFactor = useSelector(selectUiFeeFactor)!; + const isSetAcceptablePriceImpactEnabled = useSelector(selectIsSetAcceptablePriceImpactEnabled); + const selectOrdersByPositionKey = useMemo(() => makeSelectOrdersByPositionKey(positionKey), [positionKey]); + const positionOrders = useSelector(selectOrdersByPositionKey); + const market = useMarketInfo(order?.marketAddress ?? ""); + const markPrice = order ? (order.isLong ? indexToken?.prices?.minPrice : indexToken?.prices?.maxPrice) : undefined; + const positionIndexToken = order?.indexToken; + + const [isTpSlEnabled, setIsTpSlEnabled] = useState(false); + const [tpPriceInputValue, setTpPriceInputValue] = useState(""); + const [slPriceInputValue, setSlPriceInputValue] = useState(""); + + useEffect(() => { + setIsTpSlEnabled(false); + setTpPriceInputValue(""); + setSlPriceInputValue(""); + }, [order?.key]); + + const isLimitOrStopIncrease = order + ? isLimitIncreaseOrderType(order.orderType) || isStopIncreaseOrderType(order.orderType) + : false; + + const resolvedSizeDeltaUsd = sizeDeltaUsd ?? order?.sizeDeltaUsd ?? 0n; + const resolvedTriggerPrice = triggerPrice ?? order?.triggerPrice; + + const totalSizeUsdForTpSl = useMemo(() => { + return calculateTotalSizeUsd({ + existingPositionSizeUsd: existingPosition?.sizeInUsd, + isLimitOrStopIncrease, + order, + sizeDeltaUsd: resolvedSizeDeltaUsd, + }); + }, [existingPosition?.sizeInUsd, isLimitOrStopIncrease, order, resolvedSizeDeltaUsd]); + + const totalSizeInTokensForTpSl = useMemo(() => { + return calculateTotalSizeInTokens({ + baseSizeInTokens: existingPosition?.sizeInTokens, + increaseSizeDeltaInTokens: increaseAmounts?.sizeDeltaInTokens, + isLimitOrStopIncrease, + order, + positionIndexToken, + sizeDeltaUsd: resolvedSizeDeltaUsd, + triggerPrice: resolvedTriggerPrice, + }); + }, [ + existingPosition?.sizeInTokens, + increaseAmounts?.sizeDeltaInTokens, + isLimitOrStopIncrease, + order, + positionIndexToken, + resolvedSizeDeltaUsd, + resolvedTriggerPrice, + ]); + + const collateralDeltaAmountForTpSl = useMemo( + () => + getCollateralDeltaAmount({ + collateralDeltaAmount: increaseAmounts?.collateralDeltaAmount, + isLimitOrStopIncrease, + order, + }), + [increaseAmounts?.collateralDeltaAmount, isLimitOrStopIncrease, order] + ); + + const totalCollateralAmountForTpSl = (existingPosition?.collateralAmount ?? 0n) + collateralDeltaAmountForTpSl; + + const collateralDeltaUsdForTpSl = useMemo( + () => + getCollateralDeltaUsd({ + collateralDeltaAmount: collateralDeltaAmountForTpSl, + collateralDeltaUsd: increaseAmounts?.collateralDeltaUsd, + isLimitOrStopIncrease, + order, + }), + [collateralDeltaAmountForTpSl, increaseAmounts?.collateralDeltaUsd, isLimitOrStopIncrease, order] + ); + + const totalCollateralUsdForTpSl = (existingPosition?.collateralUsd ?? 0n) + collateralDeltaUsdForTpSl; + + const positionForTpSl = useMemo(() => { + return getPositionForTpSl({ + existingPosition, + isLimitOrStopIncrease, + markPrice, + market, + nextPositionValuesForIncrease, + order, + positionKey, + resolvedTriggerPrice, + totalCollateralAmountForTpSl, + totalCollateralUsdForTpSl, + totalSizeInTokensForTpSl, + totalSizeUsdForTpSl, + }); + }, [ + existingPosition, + isLimitOrStopIncrease, + markPrice, + market, + nextPositionValuesForIncrease, + order, + positionKey, + resolvedTriggerPrice, + totalCollateralAmountForTpSl, + totalCollateralUsdForTpSl, + totalSizeInTokensForTpSl, + totalSizeUsdForTpSl, + ]); + + const tpSlPositionData = useMemo( + () => + buildTpSlInputPositionData({ + position: positionForTpSl, + indexTokenDecimals: positionForTpSl?.marketInfo.indexToken.decimals ?? 18, + visualMultiplier: indexToken?.visualMultiplier, + }), + [indexToken?.visualMultiplier, positionForTpSl] + ); + + const tpPriceEntry = useMemo( + () => getDefaultEntryField(USD_DECIMALS, { input: tpPriceInputValue }, indexToken?.visualMultiplier), + [indexToken?.visualMultiplier, tpPriceInputValue] + ); + + const slPriceEntry = useMemo( + () => getDefaultEntryField(USD_DECIMALS, { input: slPriceInputValue }, indexToken?.visualMultiplier), + [indexToken?.visualMultiplier, slPriceInputValue] + ); + + const tpEntry = useMemo( + () => + buildTpSlEntry({ + existingPosition, + id: "tp", + isLimitOrStopIncrease, + isTpSlEnabled, + markPrice, + order, + positionForTpSl, + priceEntry: tpPriceEntry, + triggerPrice: resolvedTriggerPrice, + }), + [ + existingPosition, + isLimitOrStopIncrease, + isTpSlEnabled, + markPrice, + positionForTpSl, + resolvedTriggerPrice, + tpPriceEntry, + order, + ] + ); + + const slEntry = useMemo( + () => + buildTpSlEntry({ + existingPosition, + id: "sl", + isLimitOrStopIncrease, + isTpSlEnabled, + markPrice, + order, + positionForTpSl, + priceEntry: slPriceEntry, + triggerPrice: resolvedTriggerPrice, + }), + [ + existingPosition, + isLimitOrStopIncrease, + isTpSlEnabled, + markPrice, + positionForTpSl, + resolvedTriggerPrice, + slPriceEntry, + order, + ] + ); + + const tpDecreaseAmounts = useMemo(() => { + if (!isTpSlEnabled) { + return undefined; + } + + return getTpSlDecreaseAmounts({ + isSetAcceptablePriceImpactEnabled, + position: positionForTpSl, + closeSizeUsd: positionForTpSl?.sizeInUsd, + triggerPrice: tpEntry.price.value, + triggerOrderType: OrderType.LimitDecrease, + isLimit: true, + limitPrice: resolvedTriggerPrice ?? order?.triggerPrice, + minCollateralUsd, + minPositionSizeUsd, + uiFeeFactor, + userReferralInfo, + }); + }, [ + isSetAcceptablePriceImpactEnabled, + isTpSlEnabled, + minCollateralUsd, + minPositionSizeUsd, + order?.triggerPrice, + positionForTpSl, + resolvedTriggerPrice, + tpEntry.price.value, + uiFeeFactor, + userReferralInfo, + ]); + + const slDecreaseAmounts = useMemo(() => { + if (!isTpSlEnabled) { + return undefined; + } + + return getTpSlDecreaseAmounts({ + isSetAcceptablePriceImpactEnabled, + position: positionForTpSl, + closeSizeUsd: positionForTpSl?.sizeInUsd, + triggerPrice: slEntry.price.value, + triggerOrderType: OrderType.StopLossDecrease, + isLimit: true, + limitPrice: resolvedTriggerPrice ?? order?.triggerPrice, + minCollateralUsd, + minPositionSizeUsd, + uiFeeFactor, + userReferralInfo, + }); + }, [ + isSetAcceptablePriceImpactEnabled, + isTpSlEnabled, + minCollateralUsd, + minPositionSizeUsd, + order?.triggerPrice, + positionForTpSl, + resolvedTriggerPrice, + slEntry.price.value, + uiFeeFactor, + userReferralInfo, + ]); + + const getSidecarExecutionFee = useCallback( + (decreaseSwapType: DecreasePositionSwapType | undefined) => { + if (!gasLimits || !tokensData || gasPrice === undefined) return undefined; + + const estimatedGas = estimateExecuteDecreaseOrderGasLimit(gasLimits, { + decreaseSwapType, + swapsCount: 0, + }); + + const oraclePriceCount = estimateOrderOraclePriceCount(0); + + return getExecutionFee(chainId as any, gasLimits, tokensData, estimatedGas, gasPrice, oraclePriceCount); + }, + [chainId, gasLimits, gasPrice, tokensData] + ); + + const tpExecutionFee = useMemo(() => { + if (!isTpSlEnabled || !tpDecreaseAmounts) return undefined; + return getSidecarExecutionFee(tpDecreaseAmounts.decreaseSwapType); + }, [getSidecarExecutionFee, isTpSlEnabled, tpDecreaseAmounts]); + + const slExecutionFee = useMemo(() => { + if (!isTpSlEnabled || !slDecreaseAmounts) return undefined; + return getSidecarExecutionFee(slDecreaseAmounts.decreaseSwapType); + }, [getSidecarExecutionFee, isTpSlEnabled, slDecreaseAmounts]); + + const sidecarExecutionFee: Pick | undefined = useMemo(() => { + const fees = [tpExecutionFee, slExecutionFee].filter(Boolean); + if (!fees.length) return undefined; + + const feeToken = fees[0]!.feeToken; + const feeTokenAmount = fees.reduce((acc, fee) => acc + (fee?.feeTokenAmount ?? 0n), 0n); + const feeUsd = fees.reduce((acc, fee) => acc + (fee?.feeUsd ?? 0n), 0n); + + return { + feeToken, + feeTokenAmount, + feeUsd, + }; + }, [slExecutionFee, tpExecutionFee]); + + const autoCancelOrdersLimit = useMemo(() => { + if (!isAutoCancelTPSL || maxAutoCancelOrders === undefined) { + return 0; + } + + const existingAutoCancel = positionOrders?.filter((o) => o.autoCancel)?.length ?? 0; + const left = Number(maxAutoCancelOrders) - existingAutoCancel; + return left > 0 ? left : 0; + }, [isAutoCancelTPSL, maxAutoCancelOrders, positionOrders]); + + const tpSlCreatePayloads = useMemo(() => { + if (!isTpSlEnabled) { + return []; + } + + return buildTpSlCreatePayloads({ + autoCancelOrdersLimit, + chainId, + account: order?.account, + marketAddress: order?.marketInfo.marketTokenAddress, + indexTokenAddress: order?.indexToken.address, + collateralTokenAddress: order?.targetCollateralToken.address, + isLong: order?.isLong, + entries: [ + { amounts: tpDecreaseAmounts, executionFeeAmount: tpExecutionFee?.feeTokenAmount }, + { amounts: slDecreaseAmounts, executionFeeAmount: slExecutionFee?.feeTokenAmount }, + ], + userReferralCode: userReferralInfo?.referralCodeForTxn, + }); + }, [ + autoCancelOrdersLimit, + chainId, + isTpSlEnabled, + order?.account, + order?.indexToken.address, + order?.isLong, + order?.marketInfo.marketTokenAddress, + order?.targetCollateralToken.address, + slDecreaseAmounts, + slExecutionFee?.feeTokenAmount, + tpDecreaseAmounts, + tpExecutionFee?.feeTokenAmount, + userReferralInfo?.referralCodeForTxn, + ]); + + const { hasTpSlValues, tpSlError, tpSlHasError } = useMemo( + () => + getTpSlErrorState({ + isTpSlEnabled, + positionForTpSl, + sl: { + price: slPriceEntry.value, + priceError: slPriceEntry.error, + decreaseAmounts: slDecreaseAmounts, + }, + tp: { + price: tpPriceEntry.value, + priceError: tpPriceEntry.error, + decreaseAmounts: tpDecreaseAmounts, + }, + }), + [ + isTpSlEnabled, + positionForTpSl, + slDecreaseAmounts, + slPriceEntry.error, + slPriceEntry.value, + tpDecreaseAmounts, + tpPriceEntry.error, + tpPriceEntry.value, + ] + ); + + return { + isLimitOrStopIncrease, + isTpSlEnabled, + setIsTpSlEnabled, + tpEntry, + slEntry, + tpSlPositionData, + tpSlCreatePayloads, + sidecarExecutionFee, + tpSlError, + tpSlHasError, + hasTpSlValues, + setTpPriceInputValue, + setSlPriceInputValue, + }; +} + +function getPositionForTpSl(p: { + existingPosition: PositionInfo | undefined; + isLimitOrStopIncrease: boolean; + markPrice: bigint | undefined; + market: ReturnType; + nextPositionValuesForIncrease: NextPositionValues | undefined; + order: PositionOrderInfo | undefined; + positionKey: string | undefined; + resolvedTriggerPrice: bigint | undefined; + totalCollateralAmountForTpSl: bigint; + totalCollateralUsdForTpSl: bigint; + totalSizeInTokensForTpSl: bigint; + totalSizeUsdForTpSl: bigint; +}): PositionInfoLoaded | undefined { + if (!p.isLimitOrStopIncrease || !p.market || !p.order || !p.positionKey) { + return undefined; + } + + const nextEntryPrice = + p.nextPositionValuesForIncrease?.nextEntryPrice ?? + p.existingPosition?.entryPrice ?? + p.resolvedTriggerPrice ?? + p.order.triggerPrice; + const nextLiqPrice = p.nextPositionValuesForIncrease?.nextLiqPrice ?? p.existingPosition?.liquidationPrice; + const baseMarkPrice = p.markPrice ?? p.existingPosition?.markPrice ?? 0n; + const nextLeverage = p.nextPositionValuesForIncrease?.nextLeverage; + + return buildTpSlPositionInfo({ + isIncrease: true, + positionKey: p.positionKey, + marketInfo: p.market, + collateralToken: p.order.targetCollateralToken, + isLong: p.order.isLong, + sizeDeltaUsd: p.totalSizeUsdForTpSl, + sizeDeltaInTokens: p.totalSizeInTokensForTpSl, + collateralDeltaAmount: p.totalCollateralAmountForTpSl, + markPrice: baseMarkPrice, + entryPrice: nextEntryPrice, + liquidationPrice: nextLiqPrice, + collateralUsd: p.totalCollateralUsdForTpSl, + remainingCollateralUsd: p.totalCollateralUsdForTpSl, + remainingCollateralAmount: p.totalCollateralAmountForTpSl, + netValue: p.totalCollateralUsdForTpSl, + leverage: nextLeverage, + leverageWithPnl: nextLeverage, + leverageWithoutPnl: nextLeverage, + existingPosition: p.existingPosition, + includeExistingFees: true, + requirePositiveSizes: true, + }); +} + +function buildTpSlEntry(p: { + existingPosition: PositionInfo | undefined; + id: "tp" | "sl"; + isLimitOrStopIncrease: boolean; + isTpSlEnabled: boolean; + markPrice: bigint | undefined; + order: PositionOrderInfo | undefined; + positionForTpSl: PositionInfoLoaded | undefined; + priceEntry: EntryField; + triggerPrice: bigint | undefined; +}): SidecarSlTpOrderEntry { + const sizeUsdEntry = getDefaultEntryField(USD_DECIMALS, { value: p.positionForTpSl?.sizeInUsd ?? null }); + const percentageEntry = getDefaultEntryField(PERCENTAGE_DECIMALS, { value: MAX_PERCENTAGE }); + + const entry: SidecarSlTpOrderEntry = { + id: p.id, + price: p.priceEntry, + sizeUsd: sizeUsdEntry, + percentage: percentageEntry, + mode: "keepPercentage", + order: null, + txnType: p.priceEntry.value ? "create" : null, + decreaseAmounts: undefined, + increaseAmounts: undefined, + }; + + if (!p.isLimitOrStopIncrease || !p.isTpSlEnabled) { + return entry; + } + + return handleEntryError(entry, p.id, { + liqPrice: p.positionForTpSl?.liquidationPrice, + triggerPrice: p.triggerPrice ?? p.order?.triggerPrice, + markPrice: p.markPrice, + isLong: p.order?.isLong, + isLimit: true, + isExistingPosition: Boolean(p.existingPosition), + }); +} + +export type TpSlValidationInput = { + price: bigint | null | undefined; + priceError?: string | null; + decreaseAmounts?: DecreasePositionAmounts; +}; + +export function getTpSlErrorState(p: { + isTpSlEnabled: boolean; + positionForTpSl: PositionInfoLoaded | undefined; + sl: TpSlValidationInput; + tp: TpSlValidationInput; +}) { + const hasTpSlValues = typeof p.tp.price === "bigint" || typeof p.sl.price === "bigint"; + const hasEntryErrors = Boolean(p.tp.priceError) || Boolean(p.sl.priceError); + const hasInvalidTp = !p.tp.decreaseAmounts && typeof p.tp.price === "bigint"; + const hasInvalidSl = !p.sl.decreaseAmounts && typeof p.sl.price === "bigint"; + + const tpSlHasError = p.isTpSlEnabled && (!p.positionForTpSl || hasEntryErrors || hasInvalidTp || hasInvalidSl); + + let tpSlError: string | undefined; + + if (p.isTpSlEnabled && !hasTpSlValues) { + tpSlError = t`Enter TP/SL price`; + } else if (tpSlHasError) { + tpSlError = t`There are issues in the TP/SL orders.`; + } + + return { + hasTpSlValues, + tpSlError, + tpSlHasError, + }; +} diff --git a/src/components/OrderEditorContainer/OrderEditorContainer.tsx b/src/components/OrderEditorContainer/OrderEditorContainer.tsx index 0555d71a3b..0faa2d0616 100644 --- a/src/components/OrderEditorContainer/OrderEditorContainer.tsx +++ b/src/components/OrderEditorContainer/OrderEditorContainer.tsx @@ -12,9 +12,25 @@ export function OrderEditorContainer() { const handleClose = useMemo(() => () => setEditingOrderState(undefined), [setEditingOrderState]); + const handleBack = useMemo( + () => () => { + setEditingOrderState({ orderKey: undefined, source: "TPSLModal" }); + }, + [setEditingOrderState] + ); + if (!editingOrderState || !editingOrder) { return null; } - return ; + const shouldShowBack = editingOrderState.source === "TPSLModal"; + + return ( + + ); } diff --git a/src/components/PercentageInput/PercentageInput.tsx b/src/components/PercentageInput/PercentageInput.tsx index 857acfb29c..01d19ba243 100644 --- a/src/components/PercentageInput/PercentageInput.tsx +++ b/src/components/PercentageInput/PercentageInput.tsx @@ -129,7 +129,7 @@ export default function PercentageInput({ setValue={handleChange} placeholder={getValueText(defaultValue)} suggestionList={suggestions} - symbol="%" + suffix="%" onPanelVisibleChange={setIsPanelVisible} inputId={inputId} isError={!!error} diff --git a/src/components/PoolSelector2/PoolSelector2.tsx b/src/components/PoolSelector2/PoolSelector2.tsx index be13c144a2..a79b3f48e1 100644 --- a/src/components/PoolSelector2/PoolSelector2.tsx +++ b/src/components/PoolSelector2/PoolSelector2.tsx @@ -34,6 +34,14 @@ type Props = { }; tradeType: TradeType; onSelect: (marketAddress: string) => void; + // eslint-disable-next-line react/no-unused-prop-types + handleClassName?: string; + // eslint-disable-next-line react/no-unused-prop-types + chevronClassName?: string; + // eslint-disable-next-line react/no-unused-prop-types + wrapperClassName?: string; + // eslint-disable-next-line react/no-unused-prop-types + popoverReferenceRef?: React.RefObject; }; export function PoolSelector2(props: Props) { @@ -41,7 +49,16 @@ export function PoolSelector2(props: Props) { const disabled = props.options?.length === 1; return ( - + {isMobile ? : } ); diff --git a/src/components/PositionDropdown/PositionDropdown.tsx b/src/components/PositionDropdown/PositionDropdown.tsx index 6633121c7b..eb56be714f 100644 --- a/src/components/PositionDropdown/PositionDropdown.tsx +++ b/src/components/PositionDropdown/PositionDropdown.tsx @@ -11,6 +11,7 @@ import MenuDotsIcon from "img/ic_menu_dots.svg?react"; import SelectIcon from "img/ic_select.svg?react"; import ShareIcon from "img/ic_share.svg?react"; import TriggerClose from "img/ic_triggerclose_16.svg?react"; +import IncreaseStopMarket from "img/tokens/ic_box_chevron_up.svg?react"; import "./PositionDropdown.css"; @@ -21,7 +22,9 @@ type Props = { handleMarketIncreaseSize?: () => void; handleLimitIncreaseSize?: () => void; handleStopMarketIncreaseSize?: () => void; + handleTwapIncreaseSize?: () => void; handleTriggerClose?: () => void; + disabled?: boolean; }; export default function PositionDropdown({ @@ -31,7 +34,9 @@ export default function PositionDropdown({ handleMarketIncreaseSize, handleLimitIncreaseSize, handleStopMarketIncreaseSize, + handleTwapIncreaseSize, handleTriggerClose, + disabled, }: Props) { const { refs, floatingStyles } = useFloating({ middleware: [offset({ mainAxis: 8 }), flip(), shift()], @@ -39,6 +44,14 @@ export default function PositionDropdown({ whileElementsMounted: autoUpdate, }); + if (disabled) { + return ( + + ); + } + return ( @@ -100,13 +113,23 @@ export default function PositionDropdown({ {handleStopMarketIncreaseSize && (
- +

Increase Size (Stop Market)

)} + {handleTwapIncreaseSize && ( + +
+ +

+ Increase Size (TWAP) +

+
+
+ )} {handleTriggerClose && (
diff --git a/src/components/PositionItem/PositionItem.tsx b/src/components/PositionItem/PositionItem.tsx index d35cea60fd..5d0d6dbe87 100644 --- a/src/components/PositionItem/PositionItem.tsx +++ b/src/components/PositionItem/PositionItem.tsx @@ -1,37 +1,26 @@ import { Trans, t } from "@lingui/macro"; import cx from "classnames"; -import { useCallback, useMemo } from "react"; +import { useCallback, useMemo, useState } from "react"; import Skeleton from "react-loading-skeleton"; import { useSettings } from "context/SettingsContext/SettingsContextProvider"; import { usePositionsConstants } from "context/SyntheticsStateContext/hooks/globalsHooks"; -import { useEditingOrderState } from "context/SyntheticsStateContext/hooks/orderEditorHooks"; -import { useCancelOrder, usePositionOrdersWithErrors } from "context/SyntheticsStateContext/hooks/orderHooks"; -import { selectOracleSettings } from "context/SyntheticsStateContext/selectors/globalSelectors"; import { selectShowPnlAfterFees } from "context/SyntheticsStateContext/selectors/settingsSelectors"; import { makeSelectMarketPriceDecimals } from "context/SyntheticsStateContext/selectors/statsSelectors"; import { selectTradeboxSelectedPositionKey } from "context/SyntheticsStateContext/selectors/tradeboxSelectors"; import { useSelector } from "context/SyntheticsStateContext/utils"; import { getBorrowingFeeRateUsd, getFundingFeeRateUsd } from "domain/synthetics/fees"; -import { - OrderErrors, - PositionOrderInfo, - isIncreaseOrderType, - isMarketOrderType, - isTwapOrder, -} from "domain/synthetics/orders"; -import { useDisabledCancelMarketOrderMessage } from "domain/synthetics/orders/useDisabledCancelMarketOrderMessage"; import { PositionInfo, formatEstimatedLiquidationTime, formatLeverage, formatLiquidationPrice, getEstimatedLiquidationTimeInHours, - getNameByOrderType, } from "domain/synthetics/positions"; import { TradeMode } from "domain/synthetics/trade"; +import { OrderOption } from "domain/synthetics/trade/usePositionSellerState"; import { CHART_PERIODS } from "lib/legacy"; -import { calculateDisplayDecimals, formatBalanceAmount, formatDeltaUsd, formatUsd } from "lib/numbers"; +import { formatBalanceAmount, formatDeltaUsd, formatUsd } from "lib/numbers"; import { getPositiveOrNegativeClass } from "lib/utils"; import { getMarketIndexName } from "sdk/utils/markets"; @@ -44,13 +33,14 @@ import { TableTd, TableTr } from "components/Table/Table"; import TokenIcon from "components/TokenIcon/TokenIcon"; import TooltipWithPortal from "components/Tooltip/TooltipWithPortal"; -import ChevronRightIcon from "img/ic_chevron_right.svg?react"; -import CloseIcon from "img/ic_close.svg?react"; import EditIcon from "img/ic_edit.svg?react"; import NewLinkThinIcon from "img/ic_new_link_thin.svg?react"; import SpinnerIcon from "img/ic_spinner.svg?react"; -import { TwapOrderProgress } from "../OrderItem/OrderItem"; +import { PositionItemOrdersLarge, PositionItemOrdersSmall } from "./PositionItemOrders"; +import { PositionItemTPSLCell } from "./PositionItemTPSLCell"; +import { AddTPSLModal } from "../TPSLModal/AddTPSLModal"; +import { TPSLModal } from "../TPSLModal/TPSLModal"; import "./PositionItem.scss"; @@ -58,7 +48,7 @@ export type Props = { position: PositionInfo; hideActions?: boolean; showPnlAfterFees: boolean; - onClosePositionClick?: () => void; + onClosePositionClick?: (orderOption?: OrderOption) => void; onEditCollateralClick?: () => void; onShareClick?: () => void; onSelectPositionClick?: (tradeMode?: TradeMode, showCurtain?: boolean) => void; @@ -75,9 +65,38 @@ export function PositionItem(p: Props) { const { minCollateralUsd } = usePositionsConstants(); const tradeboxSelectedPositionKey = useSelector(selectTradeboxSelectedPositionKey); const isCurrentMarket = tradeboxSelectedPositionKey === p.position.key; + const [showSizeInTokens, setShowSizeInTokens] = useState(false); + const [isTPSLModalVisible, setIsTPSLModalVisible] = useState(false); + const [isAddTPSLModalVisible, setIsAddTPSLModalVisible] = useState(false); + const isActionsDisabled = p.position.isOpening; + const isCloseDisabled = isActionsDisabled || p.position.sizeInUsd == 0n; const marketDecimals = useSelector(makeSelectMarketPriceDecimals(p.position.market.indexTokenAddress)); + const handleSizeClick = useCallback(() => { + setShowSizeInTokens((prev) => !prev); + }, []); + + const handleOpenTPSLModal = useCallback(() => { + setIsAddTPSLModalVisible(false); + setIsTPSLModalVisible(true); + }, []); + + const handleOpenAddTPSLModal = useCallback(() => { + setIsTPSLModalVisible(false); + setIsAddTPSLModalVisible(true); + }, []); + + const handleAddTPSLBack = useCallback(() => { + setIsAddTPSLModalVisible(false); + setIsTPSLModalVisible(true); + }, []); + + const handleAddTPSLSuccess = useCallback(() => { + setIsAddTPSLModalVisible(false); + setIsTPSLModalVisible(true); + }, []); + function renderNetValue() { return ( Click on the position to select it, then use the trade box to increase it.

- Use the "Close" button to reduce your position via market, TP/SL, or TWAP orders. + Use the TP/SL button to set TP/SL orders. +
+
+ Use the "Close" button to reduce your position via market or TWAP orders.
{showDebugValues && ( @@ -501,7 +523,15 @@ export function PositionItem(p: Props) {
- {formatUsd(p.position.sizeInUsd)} + + {showSizeInTokens + ? formatBalanceAmount( + p.position.sizeInTokens, + p.position.indexToken.decimals, + p.position.indexToken.symbol + ) + : formatUsd(p.position.sizeInUsd)} +
@@ -523,7 +553,7 @@ export function PositionItem(p: Props) { onClick={p.onShareClick} > {formatDeltaUsd(displayedPnl, displayedPnlPercentage)} - {p.onShareClick && } + {p.onShareClick && }
)}
@@ -559,36 +589,54 @@ export function PositionItem(p: Props) { {/* liqPrice */} {renderLiquidationPrice()} - {/* Close */} {!p.hideActions && ( - <> - {!p.position.isOpening ? ( - -
- - - p.onSelectPositionClick?.()} - handleMarketIncreaseSize={() => p.onSelectPositionClick?.(TradeMode.Market)} - handleShare={p.onShareClick} - handleLimitIncreaseSize={() => p.onSelectPositionClick?.(TradeMode.Limit)} - handleStopMarketIncreaseSize={() => p.onSelectPositionClick?.(TradeMode.StopMarket)} - handleTriggerClose={() => p.onSelectPositionClick?.(TradeMode.Trigger)} - /> -
-
- ) : ( - - )} - + + + + )} + {!p.hideActions && ( + +
+
+ + +
+ + p.onSelectPositionClick?.()} + handleMarketIncreaseSize={() => p.onSelectPositionClick?.(TradeMode.Market)} + handleShare={p.onShareClick} + handleLimitIncreaseSize={() => p.onSelectPositionClick?.(TradeMode.Limit)} + handleStopMarketIncreaseSize={() => p.onSelectPositionClick?.(TradeMode.StopMarket)} + handleTwapIncreaseSize={() => p.onSelectPositionClick?.(TradeMode.Twap)} + handleTriggerClose={handleOpenAddTPSLModal} + disabled={isActionsDisabled} + /> +
+
)} ); @@ -643,10 +691,18 @@ export function PositionItem(p: Props) {
-
+
Size
-
{formatUsd(p.position.sizeInUsd)}
+
+ {showSizeInTokens + ? formatBalanceAmount( + p.position.sizeInTokens, + p.position.indexToken.decimals, + p.position.indexToken.symbol + ) + : formatUsd(p.position.sizeInUsd)} +
@@ -669,7 +725,7 @@ export function PositionItem(p: Props) { onClick={p.onShareClick} > {formatDeltaUsd(displayedPnl, displayedPnlPercentage)} - {p.onShareClick && } + {p.onShareClick && }
@@ -709,6 +765,24 @@ export function PositionItem(p: Props) {
{renderLiquidationPrice()}
+ {!p.hideActions && ( +
+
+ TP/SL +
+
+ +
+
+ )}
@@ -722,32 +796,33 @@ export function PositionItem(p: Props) {
- -
- {!p.position.isOpening && !p.hideActions && ( - p.onSelectPositionClick?.()} - handleMarketIncreaseSize={() => p.onSelectPositionClick?.(TradeMode.Market, true)} - handleShare={p.onShareClick} - handleLimitIncreaseSize={() => p.onSelectPositionClick?.(TradeMode.Limit, true)} - handleStopMarketIncreaseSize={() => p.onSelectPositionClick?.(TradeMode.StopMarket, true)} - /> - )} + p.onSelectPositionClick?.()} + handleMarketIncreaseSize={() => p.onSelectPositionClick?.(TradeMode.Market, true)} + handleShare={p.onShareClick} + handleLimitIncreaseSize={() => p.onSelectPositionClick?.(TradeMode.Limit, true)} + handleStopMarketIncreaseSize={() => p.onSelectPositionClick?.(TradeMode.StopMarket, true)} + handleTwapIncreaseSize={() => p.onSelectPositionClick?.(TradeMode.Twap, true)} + handleTriggerClose={handleOpenAddTPSLModal} + disabled={isActionsDisabled} + />
@@ -756,186 +831,17 @@ export function PositionItem(p: Props) { ); } - return p.isLarge ? renderLarge() : renderSmall(); -} - -function PositionItemOrdersSmall({ - positionKey, - onOrdersClick, -}: { - positionKey: string; - onOrdersClick?: (key?: string) => void; -}) { - const ordersWithErrors = usePositionOrdersWithErrors(positionKey); - - if (ordersWithErrors.length === 0) return null; - return ( -
- {ordersWithErrors.map((params) => ( - - ))} -
- ); -} - -function PositionItemOrdersLarge({ - positionKey, - onOrdersClick, -}: { - isSmall?: boolean; - positionKey: string; - onOrdersClick?: (key?: string) => void; -}) { - const ordersWithErrors = usePositionOrdersWithErrors(positionKey); - - const [ordersErrorList, ordersWarningsList] = useMemo(() => { - const ordersErrorList = ordersWithErrors.filter(({ orderErrors }) => orderErrors.level === "error"); - const ordersWarningsList = ordersWithErrors.filter(({ orderErrors }) => orderErrors.level === "warning"); - return [ordersErrorList, ordersWarningsList]; - }, [ordersWithErrors]); - - if (ordersWithErrors.length === 0) return null; - - return ( -
- - Orders ({ordersWithErrors.length}) - {ordersWarningsList.length > 0 || ordersErrorList.length > 0 ? ( -
0 && !ordersErrorList.length, - "bg-red-500": ordersErrorList.length > 0, - })} - /> - ) : null} - - } - position="bottom" - handleClassName={cx([ - "Exchange-list-info-label", - "Exchange-position-list-orders", - "clickable", - "text-typography-secondary", - ])} - maxAllowedWidth={370} - tooltipClassName="!z-10 w-[370px]" - content={ -
-
- Active Orders -
- {ordersWithErrors.map((params) => ( - - ))} -
- } + <> + {p.isLarge ? renderLarge() : renderSmall()} + + -
- ); -} - -function PositionItemOrder({ - order, - orderErrors, - onOrdersClick, -}: { - order: PositionOrderInfo; - orderErrors: OrderErrors; - onOrdersClick?: (key?: string) => void; -}) { - const [, setEditingOrderState] = useEditingOrderState(); - const [isCancelling, cancel] = useCancelOrder(order); - const handleOrdersClick = useCallback(() => { - onOrdersClick?.(order.key); - }, [onOrdersClick, order.key]); - - const errors = orderErrors.errors; - - const handleEditClick = useCallback(() => { - setEditingOrderState({ orderKey: order.key, source: "PositionsList" }); - }, [order.key, setEditingOrderState]); - - const oracleSettings = useSelector(selectOracleSettings); - const disabledCancelMarketOrderMessage = useDisabledCancelMarketOrderMessage(order, oracleSettings); - - const isDisabled = isCancelling || Boolean(disabledCancelMarketOrderMessage); - - const cancelButton = ( - - ); - - return ( -
-
- - {!isTwapOrder(order) && !isMarketOrderType(order.orderType) && ( - - )} - {disabledCancelMarketOrderMessage ? ( - - ) : ( - cancelButton - )} -
- - {errors.length !== 0 && ( -
- {errors.map((err) => ( -
- {err.msg} -
- ))} -
- )} -
- ); -} - -function PositionItemOrderText({ order }: { order: PositionOrderInfo }) { - const triggerThresholdType = order.triggerThresholdType; - const isIncrease = isIncreaseOrderType(order.orderType); - const isTwap = isTwapOrder(order); - - return ( -
- {getNameByOrderType(order.orderType, order.isTwap, { abbr: true })} - {!isTwap && !isMarketOrderType(order.orderType) ? `: ${triggerThresholdType} ` : null} - {!isTwap && !isMarketOrderType(order.orderType) && ( - - {formatUsd(order.triggerPrice, { - displayDecimals: calculateDisplayDecimals( - order.triggerPrice, - undefined, - order.indexToken?.visualMultiplier - ), - visualMultiplier: order.indexToken?.visualMultiplier, - })} - - )} - :{" "} - - {isIncrease ? "+" : "-"} - {formatUsd(order.sizeDeltaUsd)} {isTwapOrder(order) && } - -
+ ); } diff --git a/src/components/PositionItem/PositionItemOrders.tsx b/src/components/PositionItem/PositionItemOrders.tsx new file mode 100644 index 0000000000..d8a9a1a743 --- /dev/null +++ b/src/components/PositionItem/PositionItemOrders.tsx @@ -0,0 +1,208 @@ +import { Trans } from "@lingui/macro"; +import cx from "classnames"; +import { useCallback, useMemo } from "react"; + +import { useEditingOrderState } from "context/SyntheticsStateContext/hooks/orderEditorHooks"; +import { useCancelOrder, usePositionOrdersWithErrors } from "context/SyntheticsStateContext/hooks/orderHooks"; +import { selectOracleSettings } from "context/SyntheticsStateContext/selectors/globalSelectors"; +import { useSelector } from "context/SyntheticsStateContext/utils"; +import { + OrderErrors, + PositionOrderInfo, + isIncreaseOrderType, + isMarketOrderType, + isTwapOrder, +} from "domain/synthetics/orders"; +import { useDisabledCancelMarketOrderMessage } from "domain/synthetics/orders/useDisabledCancelMarketOrderMessage"; +import { getNameByOrderType } from "domain/synthetics/positions"; +import { calculateDisplayDecimals, formatUsd } from "lib/numbers"; + +import Button from "components/Button/Button"; +import TooltipWithPortal from "components/Tooltip/TooltipWithPortal"; + +import ChevronRightIcon from "img/ic_chevron_right.svg?react"; +import CloseIcon from "img/ic_close.svg?react"; +import EditIcon from "img/ic_edit.svg?react"; + +import { TwapOrderProgress } from "../OrderItem/OrderItem"; + +export function PositionItemOrdersSmall({ + positionKey, + onOrdersClick, +}: { + positionKey: string; + onOrdersClick?: (key?: string) => void; +}) { + const ordersWithErrors = usePositionOrdersWithErrors(positionKey); + + if (ordersWithErrors.length === 0) return null; + + return ( +
+ {ordersWithErrors.map((params) => ( + + ))} +
+ ); +} + +export function PositionItemOrdersLarge({ + positionKey, + onOrdersClick, +}: { + isSmall?: boolean; + positionKey: string; + onOrdersClick?: (key?: string) => void; +}) { + const ordersWithErrors = usePositionOrdersWithErrors(positionKey); + + const [ordersErrorList, ordersWarningsList] = useMemo(() => { + const ordersErrorList = ordersWithErrors.filter(({ orderErrors }) => orderErrors.level === "error"); + const ordersWarningsList = ordersWithErrors.filter(({ orderErrors }) => orderErrors.level === "warning"); + return [ordersErrorList, ordersWarningsList]; + }, [ordersWithErrors]); + + if (ordersWithErrors.length === 0) return null; + + return ( +
+ + Orders ({ordersWithErrors.length}) + {ordersWarningsList.length > 0 || ordersErrorList.length > 0 ? ( +
0 && !ordersErrorList.length, + "bg-red-500": ordersErrorList.length > 0, + })} + /> + ) : null} + + } + position="bottom" + handleClassName={cx([ + "Exchange-list-info-label", + "Exchange-position-list-orders", + "clickable", + "text-typography-secondary", + ])} + maxAllowedWidth={370} + tooltipClassName="!z-10 w-[370px]" + content={ +
+
+ Active Orders +
+ {ordersWithErrors.map((params) => ( + + ))} +
+ } + /> +
+ ); +} + +function PositionItemOrder({ + order, + orderErrors, + onOrdersClick, +}: { + order: PositionOrderInfo; + orderErrors: OrderErrors; + onOrdersClick?: (key?: string) => void; +}) { + const [, setEditingOrderState] = useEditingOrderState(); + const [isCancelling, cancel] = useCancelOrder(order); + const handleOrdersClick = useCallback(() => { + onOrdersClick?.(order.key); + }, [onOrdersClick, order.key]); + + const errors = orderErrors.errors; + + const handleEditClick = useCallback(() => { + setEditingOrderState({ orderKey: order.key, source: "PositionsList" }); + }, [order.key, setEditingOrderState]); + + const oracleSettings = useSelector(selectOracleSettings); + const disabledCancelMarketOrderMessage = useDisabledCancelMarketOrderMessage(order, oracleSettings); + + const isDisabled = isCancelling || Boolean(disabledCancelMarketOrderMessage); + + const cancelButton = ( + + ); + + return ( +
+
+ + {!isTwapOrder(order) && !isMarketOrderType(order.orderType) && ( + + )} + {disabledCancelMarketOrderMessage ? ( + + ) : ( + cancelButton + )} +
+ + {errors.length !== 0 && ( +
+ {errors.map((err) => ( +
+ {err.msg} +
+ ))} +
+ )} +
+ ); +} + +function PositionItemOrderText({ order }: { order: PositionOrderInfo }) { + const triggerThresholdType = order.triggerThresholdType; + const isIncrease = isIncreaseOrderType(order.orderType); + const isTwap = isTwapOrder(order); + + return ( +
+ {getNameByOrderType(order.orderType, order.isTwap, { abbr: true })} + {!isTwap && !isMarketOrderType(order.orderType) ? `: ${triggerThresholdType} ` : null} + {!isTwap && !isMarketOrderType(order.orderType) && ( + + {formatUsd(order.triggerPrice, { + displayDecimals: calculateDisplayDecimals( + order.triggerPrice, + undefined, + order.indexToken?.visualMultiplier + ), + visualMultiplier: order.indexToken?.visualMultiplier, + })} + + )} + :{" "} + + {isIncrease ? "+" : "-"} + {formatUsd(order.sizeDeltaUsd)} {isTwapOrder(order) && } + +
+ ); +} diff --git a/src/components/PositionItem/PositionItemTPSLCell.tsx b/src/components/PositionItem/PositionItemTPSLCell.tsx new file mode 100644 index 0000000000..ba6696168e --- /dev/null +++ b/src/components/PositionItem/PositionItemTPSLCell.tsx @@ -0,0 +1,182 @@ +import cx from "classnames"; +import { useMemo } from "react"; + +import { usePositionOrdersWithErrors } from "context/SyntheticsStateContext/hooks/orderHooks"; +import { + PositionOrderInfo, + isLimitDecreaseOrderType, + isStopLossOrderType, + isTwapOrder, +} from "domain/synthetics/orders"; +import { formatUsd } from "lib/numbers"; + +import EditIcon from "img/ic_edit.svg?react"; +import PlusCircleIcon from "img/ic_plus_circle.svg?react"; + +export function PositionItemTPSLCell({ + positionKey, + markPrice, + marketDecimals, + visualMultiplier, + isLarge, + onOpenTPSLModal, + isDisabled = false, +}: { + positionKey: string; + markPrice: bigint; + marketDecimals: number | undefined; + visualMultiplier?: number; + isLarge: boolean; + onOpenTPSLModal: () => void; + isDisabled?: boolean; +}) { + const ordersWithErrors = usePositionOrdersWithErrors(positionKey); + + const { closestTp, tpCount, closestSl, slCount } = useMemo( + () => + getClosestTpSlOrders({ + ordersWithErrors, + markPrice, + }), + [ordersWithErrors, markPrice] + ); + + const hasTpOrSl = tpCount > 0 || slCount > 0; + + if (!isLarge) { + return ( +
+
+ + {closestTp ? ( + <> + {formatUsd(closestTp.triggerPrice, { + displayDecimals: marketDecimals, + visualMultiplier, + })} + {tpCount > 1 && ` (${tpCount})`} + + ) : ( + "—" + )} + + + + {closestSl ? ( + <> + {formatUsd(closestSl.triggerPrice, { + displayDecimals: marketDecimals, + visualMultiplier, + })} + {slCount > 1 && ` (${slCount})`} + + ) : ( + "—" + )} + +
+ +
+ ); + } + + return ( +
+
+ + {closestTp ? ( + <> + {formatUsd(closestTp.triggerPrice, { + displayDecimals: marketDecimals, + visualMultiplier, + })} + {tpCount > 1 && ({tpCount})} + + ) : ( + "—" + )} + + + {closestSl ? ( + <> + {formatUsd(closestSl.triggerPrice, { + displayDecimals: marketDecimals, + visualMultiplier, + })} + {slCount > 1 && ({slCount})} + + ) : ( + "—" + )} + +
+ +
+ ); +} + +function getClosestTpSlOrders(p: { ordersWithErrors: { order: PositionOrderInfo }[]; markPrice: bigint }): { + closestTp: PositionOrderInfo | undefined; + closestSl: PositionOrderInfo | undefined; + tpCount: number; + slCount: number; +} { + const tpOrders: PositionOrderInfo[] = []; + const slOrders: PositionOrderInfo[] = []; + + for (const { order } of p.ordersWithErrors) { + if (isTwapOrder(order)) continue; + if (isLimitDecreaseOrderType(order.orderType)) { + tpOrders.push(order); + } else if (isStopLossOrderType(order.orderType)) { + slOrders.push(order); + } + } + + let closestTp: PositionOrderInfo | undefined; + let closestTpDistance = BigInt(Number.MAX_SAFE_INTEGER) * 10n ** 30n; + for (const order of tpOrders) { + const distance = + order.triggerPrice > p.markPrice ? order.triggerPrice - p.markPrice : p.markPrice - order.triggerPrice; + if (distance < closestTpDistance) { + closestTpDistance = distance; + closestTp = order; + } + } + + let closestSl: PositionOrderInfo | undefined; + let closestSlDistance = BigInt(Number.MAX_SAFE_INTEGER) * 10n ** 30n; + for (const order of slOrders) { + const distance = + order.triggerPrice > p.markPrice ? order.triggerPrice - p.markPrice : p.markPrice - order.triggerPrice; + if (distance < closestSlDistance) { + closestSlDistance = distance; + closestSl = order; + } + } + + return { + closestTp, + tpCount: tpOrders.length, + closestSl, + slCount: slOrders.length, + }; +} diff --git a/src/components/PositionList/PositionList.tsx b/src/components/PositionList/PositionList.tsx index b680c1c0b7..2dfd4870d6 100644 --- a/src/components/PositionList/PositionList.tsx +++ b/src/components/PositionList/PositionList.tsx @@ -10,6 +10,7 @@ import { selectShowPnlAfterFees } from "context/SyntheticsStateContext/selectors import { useSelector } from "context/SyntheticsStateContext/utils"; import { PositionInfo } from "domain/synthetics/positions"; import { TradeMode } from "domain/synthetics/trade"; +import { OrderOption } from "domain/synthetics/trade/usePositionSellerState"; import { getByKey } from "lib/objects"; import { userAnalytics } from "lib/userAnalytics"; import { SharePositionClickEvent } from "lib/userAnalytics/types"; @@ -24,7 +25,7 @@ import { TableScrollFadeContainer } from "components/TableScrollFade/TableScroll type Props = { onSelectPositionClick: (key: string, tradeMode?: TradeMode, showCurtain?: boolean) => void; - onClosePositionClick: (key: string) => void; + onClosePositionClick: (key: string, orderOption?: OrderOption) => void; onOrdersClick: (positionKey: string, orderKey: string | undefined) => void; onCancelOrder: (key: string) => void; openSettings: () => void; @@ -88,33 +89,38 @@ export function PositionList(p: Props) { disableScrollFade={positions.length === 0} className="flex grow flex-col bg-slate-900" > - +
- + Position Size - + Net Value - + Collateral - + Entry Price - + Mark Price - + Liq. Price + {!hideActions && ( + + TP/SL + + )} {!isLoading && !p.hideActions && ( <> - + Close )} @@ -182,7 +188,7 @@ const PositionItemWrapper = memo( }: { position: PositionInfo; onEditCollateralClick: (positionKey: string) => void; - onClosePositionClick: (positionKey: string) => void; + onClosePositionClick: (positionKey: string, orderOption?: OrderOption) => void; onOrdersClick: (positionKey: string, orderKey: string | undefined) => void; onSelectPositionClick: (positionKey: string, tradeMode: TradeMode | undefined, showCurtain?: boolean) => void; isLarge: boolean; @@ -197,7 +203,7 @@ const PositionItemWrapper = memo( [onEditCollateralClick, position.key] ); const handleClosePositionClick = useCallback( - () => onClosePositionClick(position.key), + (orderOption?: OrderOption) => onClosePositionClick(position.key, orderOption), [onClosePositionClick, position.key] ); diff --git a/src/components/PositionSeller/PositionSeller.tsx b/src/components/PositionSeller/PositionSeller.tsx index 2aceb8fa9c..f4908d1ba2 100644 --- a/src/components/PositionSeller/PositionSeller.tsx +++ b/src/components/PositionSeller/PositionSeller.tsx @@ -1,9 +1,8 @@ -import { MessageDescriptor } from "@lingui/core"; -import { msg, t, Trans } from "@lingui/macro"; +import { t, Trans } from "@lingui/macro"; import { useConnectModal } from "@rainbow-me/rainbowkit"; import cx from "classnames"; import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react"; -import { useKey, useLatest, useMedia } from "react-use"; +import { useKey, useLatest } from "react-use"; import { USD_DECIMALS } from "config/factors"; import { UI_FEE_RECEIVER_ACCOUNT } from "config/ui"; @@ -36,7 +35,6 @@ import { selectPositionSellerSetDefaultReceiveToken, selectPositionSellerShouldSwap, selectPositionSellerSwapAmounts, - selectPositionSellerTriggerPrice, } from "context/SyntheticsStateContext/selectors/positionSellerSelectors"; import { selectExecutionFeeBufferBps } from "context/SyntheticsStateContext/selectors/settingsSelectors"; import { makeSelectMarketPriceDecimals } from "context/SyntheticsStateContext/selectors/statsSelectors"; @@ -48,13 +46,12 @@ import { useExpressOrdersParams } from "domain/synthetics/express/useRelayerFeeH import { DecreasePositionSwapType, OrderType } from "domain/synthetics/orders"; import { sendBatchOrderTxn } from "domain/synthetics/orders/sendBatchOrderTxn"; import { useOrderTxnCallbacks } from "domain/synthetics/orders/useOrderTxnCallbacks"; -import { formatLeverage, formatLiquidationPrice, getNameByOrderType } from "domain/synthetics/positions"; +import { formatLeverage, formatLiquidationPrice } from "domain/synthetics/positions"; import { getApprovalRequirements } from "domain/synthetics/tokens"; import { getPositionSellerTradeFlags } from "domain/synthetics/trade"; import { getTwapRecommendation } from "domain/synthetics/trade/twapRecommendation"; import { TradeType } from "domain/synthetics/trade/types"; import { useDebugExecutionPrice } from "domain/synthetics/trade/useExecutionPrice"; -import { useMaxAutoCancelOrdersState } from "domain/synthetics/trade/useMaxAutoCancelOrdersState"; import { ORDER_OPTION_TO_TRADE_MODE, OrderOption } from "domain/synthetics/trade/usePositionSellerState"; import { usePriceImpactWarningState } from "domain/synthetics/trade/usePriceImpactWarningState"; import { getCommonError, getDecreaseError, getExpressError } from "domain/synthetics/trade/utils/validation"; @@ -65,15 +62,7 @@ import { useDebouncedInputValue } from "lib/debounce/useDebouncedInputValue"; import { helperToast } from "lib/helperToast"; import { useLocalizedMap } from "lib/i18n"; import { initDecreaseOrderMetricData, sendOrderSubmittedMetric, sendTxnValidationErrorMetric } from "lib/metrics/utils"; -import { - calculateDisplayDecimals, - formatAmount, - formatAmountFree, - formatDeltaUsd, - formatPercentage, - formatUsd, - parseValue, -} from "lib/numbers"; +import { formatAmountFree, formatDeltaUsd, formatUsd, parseValue } from "lib/numbers"; import { useJsonRpcProvider } from "lib/rpc"; import { useHasOutdatedUi } from "lib/useHasOutdatedUi"; import useWallet from "lib/wallets/useWallet"; @@ -94,7 +83,6 @@ import BuyInputSection from "components/BuyInputSection/BuyInputSection"; import { ColorfulBanner } from "components/ColorfulBanner/ColorfulBanner"; import ExternalLink from "components/ExternalLink/ExternalLink"; import Modal from "components/Modal/Modal"; -import Tabs from "components/Tabs/Tabs"; import ToggleSwitch from "components/ToggleSwitch/ToggleSwitch"; import TokenSelector from "components/TokenSelector/TokenSelector"; import TooltipWithPortal from "components/Tooltip/TooltipWithPortal"; @@ -107,7 +95,7 @@ import { PositionSellerAdvancedRows } from "./PositionSellerAdvancedDisplayRows" import { HighPriceImpactOrFeesWarningCard } from "../HighPriceImpactOrFeesWarningCard/HighPriceImpactOrFeesWarningCard"; import { SyntheticsInfoRow } from "../SyntheticsInfoRow"; import { ExpressTradingWarningCard } from "../TradeBox/ExpressTradingWarningCard"; -import TradeInfoIcon from "../TradeInfoIcon/TradeInfoIcon"; +import { tradeModeLabels, tradeTypeLabels } from "../TradeBox/tradeboxConstants"; import TwapRows from "../TwapRows/TwapRows"; import { PositionSellerPriceImpactFeesRow } from "./rows/PositionSellerPriceImpactFeesRow"; @@ -117,12 +105,6 @@ export type Props = { setPendingTxns: (txns: any) => void; }; -const ORDER_OPTION_LABELS: Record = { - [OrderOption.Market]: msg`Market`, - [OrderOption.Trigger]: msg`TP/SL`, - [OrderOption.Twap]: msg`TWAP`, -}; - export function PositionSeller() { const [, setClosingPositionKey] = useClosingPositionKeyState(); const [isApproving, setIsApproving] = useState(false); @@ -144,7 +126,8 @@ export function PositionSeller() { const toToken = position?.indexToken; const submitButtonRef = useRef(null); const { shouldDisableValidationForTesting } = useSettings(); - const localizedOrderOptionLabels = useLocalizedMap(ORDER_OPTION_LABELS); + const localizedTradeModeLabels = useLocalizedMap(tradeModeLabels); + const localizedTradeTypeLabels = useLocalizedMap(tradeTypeLabels); const blockTimestampData = useSelector(selectBlockTimestampData); const marketsInfoData = useSelector(selectMarketsInfoData); const gasPaymentTokenAllowance = useSelector(selectGasPaymentTokenAllowance); @@ -172,8 +155,7 @@ export function PositionSeller() { setIsSubmitting, setReceiveTokenAddress, setSelectedTriggerAcceptablePriceImpactBps, - setTriggerPriceInputValue: setTriggerPriceInputValueRaw, - triggerPriceInputValue: triggerPriceInputValueRaw, + triggerPriceInputValue, resetPositionSeller, setIsReceiveTokenChanged, setKeepLeverage, @@ -182,21 +164,16 @@ export function PositionSeller() { setDuration, setNumberOfParts, } = usePositionSeller(); + const tradeMode = ORDER_OPTION_TO_TRADE_MODE[orderOption]; + const tradeType = position ? (position.isLong ? TradeType.Long : TradeType.Short) : undefined; const [closeUsdInputValue, setCloseUsdInputValue] = useDebouncedInputValue( closeUsdInputValueRaw, setCloseUsdInputValueRaw ); - const [triggerPriceInputValue, setTriggerPriceInputValue] = useDebouncedInputValue( - triggerPriceInputValueRaw, - setTriggerPriceInputValueRaw - ); const [isWaitingForDebounceBeforeSubmit, setIsWaitingForDebounceBeforeSubmit] = useState(false); - const triggerPrice = useSelector(selectPositionSellerTriggerPrice); - - const isTrigger = orderOption === OrderOption.Trigger; const isTwap = orderOption === OrderOption.Twap; const isMarket = orderOption === OrderOption.Market; const closeSizeUsd = parseValue(closeUsdInputValue || "0", USD_DECIMALS)!; @@ -270,11 +247,8 @@ export function PositionSeller() { } }, [setIsDismissedLatestRef, isVisible, orderOption]); - const { autoCancelOrdersLimit } = useMaxAutoCancelOrdersState({ positionKey: position?.key }); - const batchParams: BatchOrderTxnParams | undefined = useMemo(() => { - let orderType = isTrigger ? decreaseAmounts?.triggerOrderType : OrderType.MarketDecrease; - orderType = isTwap ? OrderType.LimitDecrease : orderType; + const orderType = isTwap ? OrderType.LimitDecrease : OrderType.MarketDecrease; // TODO findSwapPath considering decreasePositionSwapType? const swapPath = @@ -304,7 +278,7 @@ export function PositionSeller() { executionGasLimit: executionFee.gasLimit, referralCode: userReferralInfo?.referralCodeForTxn, allowedSlippage: isMarket ? allowedSlippage : 0, - autoCancel: isTrigger ? autoCancelOrdersLimit > 0 : false, + autoCancel: false, uiFeeReceiver: UI_FEE_RECEIVER_ACCOUNT, orderType, marketAddress: position.marketAddress, @@ -315,7 +289,7 @@ export function PositionSeller() { swapPath, sizeDeltaUsd: decreaseAmounts.sizeDeltaUsd, sizeDeltaInTokens: decreaseAmounts.sizeDeltaInTokens, - triggerPrice: isTrigger ? triggerPrice : undefined, + triggerPrice: undefined, acceptablePrice: decreaseAmounts.acceptablePrice, decreasePositionSwapType: decreaseAmounts.decreaseSwapType, externalSwapQuote: undefined, @@ -340,19 +314,16 @@ export function PositionSeller() { }, [ account, allowedSlippage, - autoCancelOrdersLimit, chainId, decreaseAmounts?.acceptablePrice, decreaseAmounts?.collateralDeltaAmount, decreaseAmounts?.decreaseSwapType, decreaseAmounts?.sizeDeltaInTokens, decreaseAmounts?.sizeDeltaUsd, - decreaseAmounts?.triggerOrderType, duration, executionFee?.feeTokenAmount, executionFee?.gasLimit, isMarket, - isTrigger, isTwap, marketsInfoData, numberOfParts, @@ -362,7 +333,6 @@ export function PositionSeller() { signer, swapAmounts?.swapStrategy.swapPathStats?.swapPath, tokensData, - triggerPrice, userReferralInfo?.referralCodeForTxn, ]); @@ -434,8 +404,8 @@ export function PositionSeller() { inputSizeUsd: closeSizeUsd, sizeDeltaUsd: decreaseAmounts?.sizeDeltaUsd, receiveToken, - isTrigger, - triggerPrice, + isTrigger: false, + triggerPrice: undefined, triggerThresholdType: undefined, existingPosition: position, markPrice, @@ -465,14 +435,12 @@ export function PositionSeller() { hasOutdatedUi, isNotEnoughReceiveTokenLiquidity, isSubmitting, - isTrigger, markPrice, minCollateralUsd, nextPositionValues, position, receiveToken, tokensData, - triggerPrice, minPositionSizeUsd, isTwap, numberOfParts, @@ -509,7 +477,7 @@ export function PositionSeller() { orderType: params?.orderPayload.orderType, hasReferralCode: Boolean(userReferralInfo?.referralCodeForTxn), subaccount: expressParams?.subaccount, - triggerPrice, + triggerPrice: undefined, marketInfo: position?.marketInfo, executionFeeBufferBps, isTwap, @@ -594,7 +562,7 @@ export function PositionSeller() { () => { if (isVisible && !error) { submitButtonRef.current?.scrollIntoView({ behavior: "smooth", block: "end" }); - if (closeUsdInputValue === closeUsdInputValueRaw && triggerPriceInputValue === triggerPriceInputValueRaw) { + if (closeUsdInputValue === closeUsdInputValueRaw) { latestOnSubmit.current(); } else { setIsWaitingForDebounceBeforeSubmit(true); @@ -602,15 +570,7 @@ export function PositionSeller() { } }, {}, - [ - isVisible, - error, - closeUsdInputValue, - triggerPriceInputValue, - closeUsdInputValueRaw, - triggerPriceInputValueRaw, - latestOnSubmit, - ] + [isVisible, error, closeUsdInputValue, closeUsdInputValueRaw, latestOnSubmit] ); useEffect(() => { @@ -620,22 +580,11 @@ export function PositionSeller() { }, [isApproving, tokensToApprove.length]); useEffect(() => { - if ( - isWaitingForDebounceBeforeSubmit && - closeUsdInputValue === closeUsdInputValueRaw && - triggerPriceInputValue === triggerPriceInputValueRaw - ) { + if (isWaitingForDebounceBeforeSubmit && closeUsdInputValue === closeUsdInputValueRaw) { setIsWaitingForDebounceBeforeSubmit(false); latestOnSubmit.current(); } - }, [ - isWaitingForDebounceBeforeSubmit, - latestOnSubmit, - closeUsdInputValue, - triggerPriceInputValue, - closeUsdInputValueRaw, - triggerPriceInputValueRaw, - ]); + }, [isWaitingForDebounceBeforeSubmit, latestOnSubmit, closeUsdInputValue, closeUsdInputValueRaw]); useEffect(() => { if (!isVisible) { @@ -654,7 +603,7 @@ export function PositionSeller() { ); useEffect(() => { - if (isTrigger && decreaseAmounts) { + if (isTwap && decreaseAmounts) { if ( defaultTriggerAcceptablePriceImpactBps === undefined || defaultTriggerAcceptablePriceImpactBps !== bigMath.abs(decreaseAmounts.recommendedAcceptablePriceDeltaBps) @@ -666,7 +615,7 @@ export function PositionSeller() { }, [ decreaseAmounts, defaultTriggerAcceptablePriceImpactBps, - isTrigger, + isTwap, setDefaultTriggerAcceptablePriceImpactBps, setSelectedTriggerAcceptablePriceImpactBps, ]); @@ -697,21 +646,7 @@ export function PositionSeller() { /> ); - const receiveTokenRow = isTrigger ? ( - - } - /> - ) : ( + const receiveTokenRow = ( ); - const { warning: maxAutoCancelOrdersWarning } = useMaxAutoCancelOrdersState({ - positionKey: position?.key, - isCreatingNewAutoCancel: isTrigger, - }); const leverageCheckboxDisabledByCollateral = usePositionSellerLeverageDisabledByCollateral(); const keepLeverage = usePositionSellerKeepLeverage(); const keepLeverageChecked = decreaseAmounts?.isFullClose ? false : keepLeverage ?? false; @@ -778,48 +709,17 @@ export function PositionSeller() { keepLeverageText ); - const pnlRow = - position && - (isTrigger ? ( - - {formatDeltaUsd(decreaseAmounts?.estimatedPnl)} ( - {formatPercentage(decreaseAmounts?.estimatedPnlPercentage, { signed: true })}) - - } - to={ - decreaseAmounts?.sizeDeltaUsd ? ( - <> - {formatDeltaUsd(nextPositionValues?.nextPnl)} ( - {formatPercentage(nextPositionValues?.nextPnlPercentage, { signed: true })}) - - ) : undefined - } - /> - } - /> - ) : ( - - } - /> - )); - - const tabsOptions = useMemo(() => { - return Object.values(OrderOption).map((option) => ({ - value: option, - label: localizedOrderOptionLabels[option], - })); - }, [localizedOrderOptionLabels]); + const pnlRow = position && ( + + } + /> + ); const buttonState = useMemo(() => { if (!isAllowanceLoaded) { @@ -865,26 +765,25 @@ export function PositionSeller() { return { text: error || - (isTrigger || isTwap - ? t`Create ${isTwap ? "TWAP Decrease" : getNameByOrderType(decreaseAmounts?.triggerOrderType, isTwap)} Order` + (tradeType !== undefined + ? `${localizedTradeModeLabels[tradeMode]}: ${localizedTradeTypeLabels[tradeType]} ${t`Decrease`}` : t`Close`), disabled: Boolean(error) && !shouldDisableValidationForTesting, }; }, [ chainId, - decreaseAmounts?.triggerOrderType, error, isAllowanceLoaded, isApproving, isExpressLoading, - isTrigger, - isTwap, + localizedTradeModeLabels, + localizedTradeTypeLabels, shouldDisableValidationForTesting, + tradeMode, + tradeType, tokensToApprove, ]); - const isMobile = useMedia("(max-width: 1024px)"); - return (
-
- - - -
-
{position && ( <> @@ -963,35 +846,6 @@ export function PositionSeller() { > USD - {isTrigger && ( - { - setTriggerPriceInputValueRaw( - formatAmount( - markPrice, - USD_DECIMALS, - calculateDisplayDecimals(markPrice, USD_DECIMALS, toToken?.visualMultiplier), - undefined, - undefined, - toToken?.visualMultiplier - ) - ); - }} - inputValue={triggerPriceInputValue} - onInputValueChange={(e) => { - setTriggerPriceInputValue(e.target.value); - }} - qa="trigger-input" - > - USD - - )}
@@ -1011,7 +865,6 @@ export function PositionSeller() { )}
- {isTrigger && maxAutoCancelOrdersWarning} - + - {isTrigger || isTwap ? ( + {isTwap ? ( isSetAcceptablePriceImpactEnabled ? ( acceptablePriceImpactInputRow ) : null @@ -152,7 +143,7 @@ export function PositionSellerAdvancedRows({ triggerPriceInputValue, slippageInp handle={Collateral ({position?.collateralToken?.symbol})} position="top-start" content={Initial collateral (collateral excluding borrow and funding fee).} - variant="icon" + variant="iconStroke" /> } value={ diff --git a/src/components/PositionSeller/rows/AllowedSlippageRow.tsx b/src/components/PositionSeller/rows/AllowedSlippageRow.tsx index 34377a5e7c..9616e633a5 100644 --- a/src/components/PositionSeller/rows/AllowedSlippageRow.tsx +++ b/src/components/PositionSeller/rows/AllowedSlippageRow.tsx @@ -23,7 +23,7 @@ export function AllowedSlippageRow({ { return (
diff --git a/src/components/SelectorBase/SelectorBase.tsx b/src/components/SelectorBase/SelectorBase.tsx index c613fab373..23101acdac 100644 --- a/src/components/SelectorBase/SelectorBase.tsx +++ b/src/components/SelectorBase/SelectorBase.tsx @@ -3,7 +3,7 @@ import { FloatingPortal, Placement, autoUpdate, flip, offset, shift, useFloating import { Popover } from "@headlessui/react"; import cx from "classnames"; import noop from "lodash/noop"; -import React, { PropsWithChildren, ReactNode, useCallback, useContext, useMemo, useState } from "react"; +import React, { PropsWithChildren, ReactNode, useCallback, useContext, useMemo, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { useMedia } from "react-use"; @@ -21,6 +21,7 @@ type Props = PropsWithChildren<{ handleClassName?: string; chevronClassName?: string; desktopPanelClassName?: string; + wrapperClassName?: string; label: ReactNode | string | undefined; modalLabel: string; disabled?: boolean; @@ -30,6 +31,7 @@ type Props = PropsWithChildren<{ popoverPlacement?: Placement; footerContent?: ReactNode; qa?: string; + popoverReferenceRef?: React.RefObject; }>; type SelectorContextType = { close: () => void; mobileHeader?: HTMLDivElement }; @@ -156,6 +158,8 @@ export function SelectorBaseMobileHeaderContent(props: PropsWithChildren) { //#endregion function SelectorBaseDesktop(props: Props & { qa?: string }) { + const buttonRef = useRef(null); + const { refs, floatingStyles } = useFloating({ middleware: [ offset({ @@ -167,17 +171,38 @@ function SelectorBaseDesktop(props: Props & { qa?: string }) { ], placement: props.popoverPlacement ?? "bottom-end", whileElementsMounted: autoUpdate, + elements: { + reference: props.popoverReferenceRef?.current ?? buttonRef.current, + }, }); const suppressPointerDown = useCallback((e: React.MouseEvent) => { e.stopPropagation(); }, []); + const setButtonRef = useCallback( + (node: HTMLElement | null) => { + buttonRef.current = node; + + if (!props.popoverReferenceRef?.current) { + refs.setReference(node); + } + }, + [props.popoverReferenceRef, refs] + ); + + const setPopoverButtonRef = useCallback( + (node: HTMLButtonElement | null) => { + setButtonRef(node); + }, + [setButtonRef] + ); + if (props.disabled) { return (
{props.label}
@@ -185,7 +210,7 @@ function SelectorBaseDesktop(props: Props & { qa?: string }) { } return ( - + {(popoverProps) => ( <> - {props.label} + {props.label}
- {props.label} + {props.label} {!props.disabled && }
diff --git a/src/components/SuggestionInput/SuggestionInput.tsx b/src/components/SuggestionInput/SuggestionInput.tsx index 3ed3a009ec..b70ed23b73 100644 --- a/src/components/SuggestionInput/SuggestionInput.tsx +++ b/src/components/SuggestionInput/SuggestionInput.tsx @@ -1,6 +1,15 @@ -import { autoUpdate, flip, offset, shift, useFloating } from "@floating-ui/react"; +import { + autoUpdate, + flip, + offset, + Placement, + shift, + useDismiss, + useFloating, + useInteractions, +} from "@floating-ui/react"; import cx from "classnames"; -import { ChangeEvent, KeyboardEvent, useCallback, useEffect, useRef, useState } from "react"; +import { ChangeEvent, KeyboardEvent, PointerEvent, useCallback, useEffect, useRef, useState } from "react"; import NumberInput from "components/NumberInput/NumberInput"; import Portal from "components/Portal/Portal"; @@ -13,7 +22,7 @@ type Props = { setValue?: (value: string) => void; placeholder?: string; suggestionList?: number[]; - symbol?: string; + suffix?: string; isError?: boolean; inputClassName?: string; onBlur?: () => void; @@ -21,6 +30,8 @@ type Props = { className?: string; label?: React.ReactNode; onPanelVisibleChange?: (isPanelVisible: boolean) => void; + suggestionWithSuffix?: boolean; + suggestionsPlacement?: Placement; }; export default function SuggestionInput({ @@ -28,7 +39,7 @@ export default function SuggestionInput({ value, setValue, suggestionList, - symbol, + suffix, isError, inputClassName, onBlur, @@ -37,6 +48,8 @@ export default function SuggestionInput({ label, onPanelVisibleChange, inputId, + suggestionWithSuffix, + suggestionsPlacement = "bottom-end", }: Props) { const inputRef = useRef(null); const [isPanelVisible, setIsPanelVisible] = useState(false); @@ -71,6 +84,12 @@ export default function SuggestionInput({ onBlur?.(); }, [onBlur]); + const handleReferencePointerDown = useCallback((e: PointerEvent) => { + e.stopPropagation(); + inputRef.current?.focus(); + setIsPanelVisible(true); + }, []); + const handleKeyDown = useCallback( (e: KeyboardEvent) => { const target = e.target as HTMLInputElement; @@ -87,18 +106,21 @@ export default function SuggestionInput({ [onKeyDown] ); - const { refs, floatingStyles } = useFloating({ + const { refs, floatingStyles, context } = useFloating({ + open: isPanelVisible, + onOpenChange: setIsPanelVisible, middleware: [offset(4), flip(), shift()], - placement: "bottom-end", + placement: suggestionsPlacement, whileElementsMounted: autoUpdate, }); + const { getReferenceProps, getFloatingProps } = useInteractions([useDismiss(context)]); return (
inputRef.current?.focus()} + className={cx("Suggestion-input flex items-baseline", className, { "input-error": isError, "pr-6": !suffix })} ref={refs.setReference} + {...getReferenceProps({ onPointerDown: handleReferencePointerDown })} > {label ? {label} : null} - {symbol && ( + {suffix && (
- {symbol} + {suffix}
)}
{suggestionList && isPanelVisible && ( -
+
    {suggestionList.map((suggestion) => ( -
  • handleSuggestionClick(suggestion)}> - {suggestion}% +
  • handleSuggestionClick(suggestion)}> + {suggestion} + {suggestionWithSuffix ? suffix : "%"}
  • ))}
diff --git a/src/components/SwipeTabs/SwipeTabs.tsx b/src/components/SwipeTabs/SwipeTabs.tsx deleted file mode 100644 index 1637f24904..0000000000 --- a/src/components/SwipeTabs/SwipeTabs.tsx +++ /dev/null @@ -1,246 +0,0 @@ -import cx from "classnames"; -import { MotionProps, MotionStyle, PanInfo, motion, useSpring } from "framer-motion"; -import { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react"; - -type Props = { - options: (string | number)[]; - option: string | number | undefined; - onChange?: (option: any) => void; - optionLabels?: Record | string[]; - optionClassnames?: Record; - icons?: Record; - qa?: string; -}; - -const MAGNETIC_SNAP_WEIGHT = 0.8; -const SWIPE_SENSITIVITY = 1.5; -const DIRECTION_THRESHOLD = 2; -const MOVEMENT_THRESHOLD = 10; - -function getTransformTemplate({ x }: Parameters>[0]) { - // Make a custom string to avoid translateZ passed by Framer Motion default - // It causes stacking context issues in iOS - return `translateX(${x})`; -} - -export function SwipeTabs({ options, option, onChange, optionLabels, icons, qa, optionClassnames }: Props) { - const containerRef = useRef(null); - const optionsRefs = useRef>({}); - const bgRef = useRef(null); - const [visuallyActiveOption, setVisuallyActiveOption] = useState(option); - const offsetRef = useRef(0); - const x = useSpring(0, { stiffness: 1000, damping: 60, bounce: 0 }); - const isDragging = useRef(false); - const isDirectionLocked = useRef(false); - - const onClick = useCallback( - (opt: string | number) => { - onChange?.(opt); - - const optElem = optionsRefs.current[options.indexOf(opt)]; - if (!optElem) return; - - const optRect = optElem.getBoundingClientRect(); - const containerRect = containerRef.current?.getBoundingClientRect(); - if (!containerRect) return; - - x.set(optRect.x - containerRect.x); - - if (bgRef.current) { - bgRef.current.style.width = `${optRect.width}px`; - } - setVisuallyActiveOption(opt); - }, - [onChange, options, x] - ); - - const getNearestOption = useCallback( - ( - atX: number, - params: { withHalfWidth: boolean; isAbsolute: boolean } = { withHalfWidth: true, isAbsolute: false } - ) => { - const containerRect = containerRef.current?.getBoundingClientRect(); - const bgRect = bgRef.current?.getBoundingClientRect(); - - if (!containerRect || !bgRect) return {}; - - const xDiff = atX + (params.withHalfWidth ? bgRect.width / 2 : 0) - (params.isAbsolute ? containerRect.x : 0); - - let acc = 0; - for (let i = 0; i < options.length; i++) { - const optionElem = optionsRefs.current[i]; - if (!optionElem) continue; - - acc += optionElem.clientWidth; - - if (acc > xDiff || i === options.length - 1) { - return { - option: options[i], - elem: optionElem, - width: optionElem.clientWidth, - offset: acc - optionElem.clientWidth, - }; - } - } - - return {}; - }, - [options] - ); - - const handlePanStart = useCallback(() => { - isDragging.current = false; - isDirectionLocked.current = false; - offsetRef.current = x.get(); - }, [x]); - - const handlePan = useCallback( - (event: PointerEvent, info: PanInfo) => { - if (!isDirectionLocked.current) { - const isHorizontal = Math.abs(info.offset.x) > Math.abs(info.offset.y) * DIRECTION_THRESHOLD; - if (Math.abs(info.offset.x) > MOVEMENT_THRESHOLD || Math.abs(info.offset.y) > MOVEMENT_THRESHOLD) { - isDirectionLocked.current = true; - isDragging.current = isHorizontal; - if (!isHorizontal) return; - } - } - - if (!isDragging.current) return; - - let newX = offsetRef.current + info.delta.x * SWIPE_SENSITIVITY; - offsetRef.current = newX; - - const { - option: nearestOption, - width, - offset, - } = getNearestOption(newX, { - withHalfWidth: true, - isAbsolute: false, - }); - - x.set((offset ?? 0) * MAGNETIC_SNAP_WEIGHT + newX * (1 - MAGNETIC_SNAP_WEIGHT)); - if (nearestOption) { - setVisuallyActiveOption(nearestOption); - - bgRef.current!.style.width = `${width}px`; - } - }, - [getNearestOption, x] - ); - - const handlePanEnd = useCallback(() => { - const { offset: targetOffset, option: nearestOption } = getNearestOption(offsetRef.current, { - withHalfWidth: true, - isAbsolute: false, - }); - - if (targetOffset !== undefined) { - isDragging.current = false; - - x.set(targetOffset); - - if (nearestOption !== option) { - setVisuallyActiveOption(nearestOption); - onChange?.(nearestOption); - } - } - }, [getNearestOption, onChange, option, x]); - - useEffect(() => { - if (!option) return; - - const optElem = optionsRefs.current[options.indexOf(option)]; - if (!optElem) return; - - const optRect = optElem.getBoundingClientRect(); - const containerRect = containerRef.current?.getBoundingClientRect(); - if (!containerRect) return; - - x.set(optRect.x - containerRect.x); - - if (bgRef.current) { - bgRef.current.style.width = `${optRect.width}px`; - } - setVisuallyActiveOption(option); - }, [option, options, x]); - - const bgStyle = useMemo( - (): MotionStyle => ({ - x, - }), - [x] - ); - - useEffect( - function handleResize() { - if (!bgRef.current) return; - - const handler = () => { - if (!option) return; - - const optElem = optionsRefs.current[options.indexOf(option)]; - if (!optElem) return; - - const containerRect = containerRef.current?.getBoundingClientRect(); - if (!containerRect) return; - - const optRect = optElem.getBoundingClientRect(); - - x.set(optRect.x - containerRect.x); - if (bgRef.current) { - bgRef.current.style.width = `${optRect.width}px`; - } - }; - - window.addEventListener("resize", handler); - - return () => { - window.removeEventListener("resize", handler); - }; - }, - [option, options, x] - ); - - return ( - - - - {options.map((opt, index) => { - const label = optionLabels && optionLabels[opt] ? optionLabels[opt] : opt; - const isActive = opt === visuallyActiveOption; - - return ( -
onClick(opt)} - key={opt} - data-qa={`${qa}-tab-${opt}`} - ref={(el) => (optionsRefs.current[index] = el)} - > - {icons && icons[opt] && {icons[opt]}} - {label} -
- ); - })} -
- ); -} diff --git a/src/components/TPSLModal/AddTPSLModal.tsx b/src/components/TPSLModal/AddTPSLModal.tsx new file mode 100644 index 0000000000..38dadff7c7 --- /dev/null +++ b/src/components/TPSLModal/AddTPSLModal.tsx @@ -0,0 +1,945 @@ +import { Trans, t } from "@lingui/macro"; +import cx from "classnames"; +import { ChangeEvent, ReactNode, useCallback, useEffect, useMemo, useState } from "react"; + +import { BASIS_POINTS_DIVISOR_BIGINT, USD_DECIMALS } from "config/factors"; +import { useSettings } from "context/SettingsContext/SettingsContextProvider"; +import { + usePositionsConstants, + useTokensData, + useUiFeeFactor, + useUserReferralInfo, +} from "context/SyntheticsStateContext/hooks/globalsHooks"; +import { + selectAccount, + selectBlockTimestampData, + selectChainId, + selectGasLimits, + selectGasPrice, + selectMarketsInfoData, + selectSrcChainId, +} from "context/SyntheticsStateContext/selectors/globalSelectors"; +import { + selectIsPnlInLeverage, + selectIsSetAcceptablePriceImpactEnabled, +} from "context/SyntheticsStateContext/selectors/settingsSelectors"; +import { useSelector } from "context/SyntheticsStateContext/utils"; +import { getIsValidExpressParams } from "domain/synthetics/express/expressOrderUtils"; +import { useExpressOrdersParams } from "domain/synthetics/express/useRelayerFeeHandler"; +import { estimateExecuteDecreaseOrderGasLimit, estimateOrderOraclePriceCount } from "domain/synthetics/fees"; +import { DecreasePositionSwapType } from "domain/synthetics/orders"; +import { sendBatchOrderTxn } from "domain/synthetics/orders/sendBatchOrderTxn"; +import { useOrderTxnCallbacks } from "domain/synthetics/orders/useOrderTxnCallbacks"; +import { + PositionInfo, + formatLeverage, + formatLiquidationPrice, + getIsPositionInfoLoaded, +} from "domain/synthetics/positions"; +import { + getDefaultEntryField, + handleEntryError, + MAX_PERCENTAGE, + PERCENTAGE_DECIMALS, +} from "domain/synthetics/sidecarOrders/utils"; +import { + getMarkPrice, + getNextPositionValuesForDecreaseTrade, + getTradeFees, + getTriggerDecreaseOrderType, + DecreasePositionAmounts, +} from "domain/synthetics/trade"; +import { useMaxAutoCancelOrdersState } from "domain/synthetics/trade/useMaxAutoCancelOrdersState"; +import { buildTpSlCreatePayloads, buildTpSlInputPositionData, getTpSlDecreaseAmounts } from "domain/tpsl/sidecar"; +import { useLocalStorageSerializeKey } from "lib/localStorage"; +import { + calculateDisplayDecimals, + expandDecimals, + formatAmount, + formatBalanceAmount, + formatDeltaUsd, + formatUsd, + parseValue, +} from "lib/numbers"; +import { useJsonRpcProvider } from "lib/rpc"; +import { getPositiveOrNegativeClass } from "lib/utils"; +import useWallet from "lib/wallets/useWallet"; +import { SidecarSlTpOrderEntry } from "sdk/types/sidecarOrders"; +import { bigMath } from "sdk/utils/bigmath"; +import { getExecutionFee } from "sdk/utils/fees/executionFee"; +import { + CreateOrderTxnParams, + DecreasePositionOrderParams, + getBatchTotalExecutionFee, +} from "sdk/utils/orderTransactions"; + +import Button from "components/Button/Button"; +import BuyInputSection from "components/BuyInputSection/BuyInputSection"; +import { ExitPriceRow } from "components/ExitPriceRow/ExitPriceRow"; +import { ExpandableRow } from "components/ExpandableRow"; +import Modal from "components/Modal/Modal"; +import { NetworkFeeRow } from "components/NetworkFeeRow/NetworkFeeRow"; +import { SyntheticsInfoRow } from "components/SyntheticsInfoRow"; +import Tabs from "components/Tabs/Tabs"; +import ToggleSwitch from "components/ToggleSwitch/ToggleSwitch"; +import TooltipWithPortal from "components/Tooltip/TooltipWithPortal"; +import { MarginPercentageSlider } from "components/TradeboxMarginFields/MarginPercentageSlider"; +import { TradeFeesRow } from "components/TradeFeesRow/TradeFeesRow"; +import { ValueTransition } from "components/ValueTransition/ValueTransition"; + +import { TPSLInputRow } from "./TPSLInputRow"; + +type Props = { + isVisible: boolean; + setIsVisible: (visible: boolean) => void; + position: PositionInfo; + onSuccess?: () => void; + onBack?: () => void; +}; + +const TP_SL_OPTIONS = [ + { value: "tp" as const, label: t`Take Profit` }, + { value: "sl" as const, label: t`Stop Loss` }, +]; + +export function AddTPSLModal({ isVisible, setIsVisible, position, onSuccess, onBack }: Props) { + const [tpPriceInput, setTpPriceInput] = useState(""); + const [slPriceInput, setSlPriceInput] = useState(""); + const [keepLeverage, setKeepLeverage] = useState(true); + const [editTPSLSize, setEditTPSLSize] = useState(false); + const [closeSizeInput, setCloseSizeInput] = useState(""); + const [closePercentage, setClosePercentage] = useState(100); + const [isSubmitting, setIsSubmitting] = useState(false); + const [executionDetailsOpen, setExecutionDetailsOpen] = useLocalStorageSerializeKey( + "add-tpsl-execution-details-open", + false + ); + const [previewTab, setPreviewTab] = useState<"tp" | "sl">("tp"); + + const chainId = useSelector(selectChainId); + const srcChainId = useSelector(selectSrcChainId); + const account = useSelector(selectAccount); + const { signer } = useWallet(); + const { provider } = useJsonRpcProvider(chainId); + const { makeOrderTxnCallback } = useOrderTxnCallbacks(); + const userReferralInfo = useUserReferralInfo(); + const { minCollateralUsd, minPositionSizeUsd } = usePositionsConstants(); + const uiFeeFactor = useUiFeeFactor(); + const { autoCancelOrdersLimit } = useMaxAutoCancelOrdersState({ positionKey: position.key }); + const tokensData = useTokensData(); + const marketsInfoData = useSelector(selectMarketsInfoData); + const gasLimits = useSelector(selectGasLimits); + const gasPrice = useSelector(selectGasPrice); + const blockTimestampData = useSelector(selectBlockTimestampData); + const isSetAcceptablePriceImpactEnabled = useSelector(selectIsSetAcceptablePriceImpactEnabled); + const isPnlInLeverage = useSelector(selectIsPnlInLeverage); + const { shouldDisableValidationForTesting } = useSettings(); + + const marketInfo = position.marketInfo; + const collateralToken = position.collateralToken; + const indexToken = position.indexToken; + const isLong = position.isLong; + + const visualMultiplier = indexToken?.visualMultiplier ?? 1; + const priceDecimals = calculateDisplayDecimals(position.markPrice, USD_DECIMALS, visualMultiplier); + + const markPrice = useMemo(() => { + return getMarkPrice({ prices: position.indexToken.prices, isLong: position.isLong, isIncrease: false }); + }, [position.indexToken.prices, position.isLong]); + + const positionData = useMemo( + () => + buildTpSlInputPositionData({ + position, + collateralUsd: position.collateralUsd, + indexTokenDecimals: indexToken?.decimals ?? 18, + visualMultiplier, + referencePrice: markPrice, + })!, + [position, indexToken?.decimals, visualMultiplier, markPrice] + ); + + const tpPriceEntry = useMemo( + () => getDefaultEntryField(USD_DECIMALS, { input: tpPriceInput }, visualMultiplier), + [tpPriceInput, visualMultiplier] + ); + + const slPriceEntry = useMemo( + () => getDefaultEntryField(USD_DECIMALS, { input: slPriceInput }, visualMultiplier), + [slPriceInput, visualMultiplier] + ); + + const tpTriggerPrice = useMemo( + () => (tpPriceEntry.value === null ? undefined : tpPriceEntry.value), + [tpPriceEntry.value] + ); + + const slTriggerPrice = useMemo( + () => (slPriceEntry.value === null ? undefined : slPriceEntry.value), + [slPriceEntry.value] + ); + + const closeSizeUsd = useMemo(() => { + if (!editTPSLSize || !closeSizeInput) { + return position.sizeInUsd; + } + return parseValue(closeSizeInput, USD_DECIMALS) ?? position.sizeInUsd; + }, [editTPSLSize, closeSizeInput, position.sizeInUsd]); + + const sizeUsdEntry = useMemo(() => getDefaultEntryField(USD_DECIMALS, { value: closeSizeUsd }), [closeSizeUsd]); + + const percentageEntry = useMemo(() => getDefaultEntryField(PERCENTAGE_DECIMALS, { value: MAX_PERCENTAGE }), []); + + const tpTriggerOrderType = useMemo(() => { + if (tpTriggerPrice === undefined || markPrice === undefined) return undefined; + return getTriggerDecreaseOrderType({ + triggerPrice: tpTriggerPrice, + markPrice, + isLong, + }); + }, [tpTriggerPrice, markPrice, isLong]); + + const slTriggerOrderType = useMemo(() => { + if (slTriggerPrice === undefined || markPrice === undefined) return undefined; + return getTriggerDecreaseOrderType({ + triggerPrice: slTriggerPrice, + markPrice, + isLong, + }); + }, [slTriggerPrice, markPrice, isLong]); + + const tpDecreaseAmounts: DecreasePositionAmounts | undefined = useMemo(() => { + if (tpTriggerPrice === undefined || !getIsPositionInfoLoaded(position)) { + return undefined; + } + + return getTpSlDecreaseAmounts({ + position, + closeSizeUsd, + triggerPrice: tpTriggerPrice, + triggerOrderType: tpTriggerOrderType, + keepLeverage, + isLimit: false, + limitPrice: undefined, + minCollateralUsd, + minPositionSizeUsd, + uiFeeFactor, + userReferralInfo, + isSetAcceptablePriceImpactEnabled, + }); + }, [ + tpTriggerPrice, + position, + closeSizeUsd, + keepLeverage, + minCollateralUsd, + minPositionSizeUsd, + uiFeeFactor, + userReferralInfo, + tpTriggerOrderType, + isSetAcceptablePriceImpactEnabled, + ]); + + const slDecreaseAmounts: DecreasePositionAmounts | undefined = useMemo(() => { + if (slTriggerPrice === undefined || !getIsPositionInfoLoaded(position)) { + return undefined; + } + + return getTpSlDecreaseAmounts({ + position, + closeSizeUsd, + triggerPrice: slTriggerPrice, + triggerOrderType: slTriggerOrderType, + keepLeverage, + isLimit: false, + limitPrice: undefined, + minCollateralUsd, + minPositionSizeUsd, + uiFeeFactor, + userReferralInfo, + isSetAcceptablePriceImpactEnabled, + }); + }, [ + slTriggerPrice, + position, + closeSizeUsd, + keepLeverage, + minCollateralUsd, + minPositionSizeUsd, + uiFeeFactor, + userReferralInfo, + slTriggerOrderType, + isSetAcceptablePriceImpactEnabled, + ]); + + const getSidecarExecutionFee = useCallback( + (decreaseSwapType: DecreasePositionSwapType | undefined) => { + if (!gasLimits || !tokensData || gasPrice === undefined) { + return undefined; + } + + const estimatedGas = estimateExecuteDecreaseOrderGasLimit(gasLimits, { + swapsCount: 0, + decreaseSwapType: decreaseSwapType ?? DecreasePositionSwapType.NoSwap, + }); + + const oraclePriceCount = estimateOrderOraclePriceCount(0); + + return getExecutionFee(chainId, gasLimits, tokensData, estimatedGas, gasPrice, oraclePriceCount); + }, + [chainId, gasLimits, gasPrice, tokensData] + ); + + const tpExecutionFee = useMemo(() => { + if (!tpDecreaseAmounts) return undefined; + return getSidecarExecutionFee(tpDecreaseAmounts.decreaseSwapType); + }, [getSidecarExecutionFee, tpDecreaseAmounts]); + + const slExecutionFee = useMemo(() => { + if (!slDecreaseAmounts) return undefined; + return getSidecarExecutionFee(slDecreaseAmounts.decreaseSwapType); + }, [getSidecarExecutionFee, slDecreaseAmounts]); + + const getFeesForDecreaseAmounts = useCallback( + (decreaseAmounts: DecreasePositionAmounts | undefined) => { + if (!decreaseAmounts || !position) { + return undefined; + } + + const sizeReductionBps = bigMath.mulDiv( + decreaseAmounts.sizeDeltaUsd, + BASIS_POINTS_DIVISOR_BIGINT, + position.sizeInUsd + ); + const collateralDeltaUsd = bigMath.mulDiv(position.collateralUsd, sizeReductionBps, BASIS_POINTS_DIVISOR_BIGINT); + + return getTradeFees({ + initialCollateralUsd: position.collateralUsd, + sizeInUsd: position.sizeInUsd, + collateralDeltaUsd, + sizeDeltaUsd: decreaseAmounts.sizeDeltaUsd, + swapSteps: [], + externalSwapQuote: undefined, + positionFeeUsd: decreaseAmounts.positionFeeUsd, + swapPriceImpactDeltaUsd: 0n, + totalPendingImpactDeltaUsd: decreaseAmounts.totalPendingImpactDeltaUsd, + increasePositionPriceImpactDeltaUsd: 0n, + priceImpactDiffUsd: decreaseAmounts.priceImpactDiffUsd, + proportionalPendingImpactDeltaUsd: decreaseAmounts.proportionalPendingImpactDeltaUsd, + decreasePositionPriceImpactDeltaUsd: decreaseAmounts.closePriceImpactDeltaUsd, + borrowingFeeUsd: decreaseAmounts.borrowingFeeUsd, + fundingFeeUsd: decreaseAmounts.fundingFeeUsd, + feeDiscountUsd: decreaseAmounts.feeDiscountUsd, + swapProfitFeeUsd: decreaseAmounts.swapProfitFeeUsd, + uiFeeFactor, + type: "decrease", + }); + }, + [position, uiFeeFactor] + ); + + const tpFees = useMemo( + () => getFeesForDecreaseAmounts(tpDecreaseAmounts), + [getFeesForDecreaseAmounts, tpDecreaseAmounts] + ); + const slFees = useMemo( + () => getFeesForDecreaseAmounts(slDecreaseAmounts), + [getFeesForDecreaseAmounts, slDecreaseAmounts] + ); + + const getNextPositionValuesForAmounts = useCallback( + (decreaseAmounts: DecreasePositionAmounts | undefined) => { + if ( + !decreaseAmounts || + decreaseAmounts.acceptablePrice === undefined || + !marketInfo || + minCollateralUsd === undefined + ) { + return undefined; + } + + return getNextPositionValuesForDecreaseTrade({ + existingPosition: position, + marketInfo, + collateralToken, + sizeDeltaUsd: decreaseAmounts.sizeDeltaUsd, + sizeDeltaInTokens: decreaseAmounts.sizeDeltaInTokens, + estimatedPnl: decreaseAmounts.estimatedPnl, + realizedPnl: decreaseAmounts.realizedPnl, + collateralDeltaUsd: decreaseAmounts.collateralDeltaUsd, + collateralDeltaAmount: decreaseAmounts.collateralDeltaAmount, + payedRemainingCollateralUsd: decreaseAmounts.payedRemainingCollateralUsd, + payedRemainingCollateralAmount: decreaseAmounts.payedRemainingCollateralAmount, + proportionalPendingImpactDeltaUsd: decreaseAmounts.proportionalPendingImpactDeltaUsd, + showPnlInLeverage: isPnlInLeverage, + isLong, + minCollateralUsd, + userReferralInfo, + }); + }, + [marketInfo, collateralToken, isLong, minCollateralUsd, position, userReferralInfo, isPnlInLeverage] + ); + + const tpNextPositionValues = useMemo( + () => getNextPositionValuesForAmounts(tpDecreaseAmounts), + [getNextPositionValuesForAmounts, tpDecreaseAmounts] + ); + const slNextPositionValues = useMemo( + () => getNextPositionValuesForAmounts(slDecreaseAmounts), + [getNextPositionValuesForAmounts, slDecreaseAmounts] + ); + + const getEstimatedPnlFromMark = useCallback( + (decreaseAmounts: DecreasePositionAmounts | undefined, triggerPrice: bigint | undefined) => { + if (!decreaseAmounts || triggerPrice === undefined || markPrice === undefined) return undefined; + + const sizeDeltaInTokens = decreaseAmounts.sizeDeltaInTokens; + if (sizeDeltaInTokens <= 0n) return undefined; + + const tokenPrecision = expandDecimals(1, indexToken?.decimals ?? 18); + const priceDiff = isLong ? triggerPrice - markPrice : markPrice - triggerPrice; + const pnlUsd = bigMath.mulDiv(priceDiff, sizeDeltaInTokens, tokenPrecision); + const pnlPercentage = + position.collateralUsd > 0n ? bigMath.mulDiv(pnlUsd, 10000n, position.collateralUsd) : undefined; + + return { + pnlUsd, + pnlPercentage, + }; + }, + [indexToken?.decimals, isLong, markPrice, position.collateralUsd] + ); + + const tpEstimatedPnl = useMemo( + () => getEstimatedPnlFromMark(tpDecreaseAmounts, tpTriggerPrice), + [getEstimatedPnlFromMark, tpDecreaseAmounts, tpTriggerPrice] + ); + + const slEstimatedPnl = useMemo( + () => getEstimatedPnlFromMark(slDecreaseAmounts, slTriggerPrice), + [getEstimatedPnlFromMark, slDecreaseAmounts, slTriggerPrice] + ); + + const getReceiveDisplay = useCallback( + (amounts: DecreasePositionAmounts | undefined) => { + if (!amounts) return undefined; + + const receiveUsd = amounts.receiveUsd ?? 0n; + const receiveAmount = amounts.receiveTokenAmount ?? 0n; + + return { + text: formatBalanceAmount(receiveAmount, collateralToken.decimals, collateralToken.symbol, { + isStable: collateralToken.isStable, + }), + usd: formatUsd(receiveUsd), + }; + }, + [collateralToken] + ); + + const tpReceiveDisplay = useMemo(() => getReceiveDisplay(tpDecreaseAmounts), [getReceiveDisplay, tpDecreaseAmounts]); + const slReceiveDisplay = useMemo(() => getReceiveDisplay(slDecreaseAmounts), [getReceiveDisplay, slDecreaseAmounts]); + + const tpPreview = useMemo( + () => ({ + decreaseAmounts: tpDecreaseAmounts, + nextPositionValues: tpNextPositionValues, + fees: tpFees, + receiveDisplay: tpReceiveDisplay, + triggerPrice: tpTriggerPrice, + }), + [tpDecreaseAmounts, tpNextPositionValues, tpFees, tpReceiveDisplay, tpTriggerPrice] + ); + + const slPreview = useMemo( + () => ({ + decreaseAmounts: slDecreaseAmounts, + nextPositionValues: slNextPositionValues, + fees: slFees, + receiveDisplay: slReceiveDisplay, + triggerPrice: slTriggerPrice, + }), + [slDecreaseAmounts, slNextPositionValues, slFees, slReceiveDisplay, slTriggerPrice] + ); + + const activePreview = previewTab === "tp" ? tpPreview : slPreview; + const activeDecreaseAmounts = activePreview.decreaseAmounts; + const activeNextPositionValues = activePreview.nextPositionValues; + const activeFees = activePreview.fees; + const activeReceiveDisplay = activePreview.receiveDisplay; + const activeTriggerPrice = activeDecreaseAmounts?.triggerPrice ?? activePreview.triggerPrice; + const activeNetPriceImpactUsd = activeFees?.collateralNetPriceImpact?.deltaUsd; + const hasPreviewData = Boolean(tpDecreaseAmounts || slDecreaseAmounts); + + const formattedMaxCloseSize = formatAmount(position.sizeInUsd, USD_DECIMALS, 2); + + const handleCloseSizeChange = useCallback((e: ChangeEvent) => { + setCloseSizeInput(e.target.value); + }, []); + + const handleClosePercentageChange = useCallback( + (percent: number) => { + setClosePercentage(percent); + const newSize = bigMath.mulDiv(position.sizeInUsd, BigInt(percent), 100n); + setCloseSizeInput(formatAmount(newSize, USD_DECIMALS, 2)); + }, + [position.sizeInUsd] + ); + + const handleSliderChange = useCallback( + (percent: number) => { + setClosePercentage(percent); + const newSize = bigMath.mulDiv(position.sizeInUsd, BigInt(percent), 100n); + setCloseSizeInput(formatAmount(newSize, USD_DECIMALS, 2)); + }, + [position.sizeInUsd] + ); + + const handleMaxClick = useCallback(() => { + setClosePercentage(100); + setCloseSizeInput(formattedMaxCloseSize); + }, [formattedMaxCloseSize]); + + const handleEditTPSLSizeToggle = useCallback( + (value: boolean) => { + setEditTPSLSize(value); + setClosePercentage(100); + setCloseSizeInput(formattedMaxCloseSize); + }, + [formattedMaxCloseSize] + ); + + const tpPriceError = useMemo(() => { + if (markPrice === undefined) return undefined; + + const entry: SidecarSlTpOrderEntry = { + id: "tp", + price: tpPriceEntry, + sizeUsd: sizeUsdEntry, + percentage: percentageEntry, + mode: "keepPercentage", + order: null, + txnType: tpPriceEntry.value ? "create" : null, + decreaseAmounts: undefined, + increaseAmounts: undefined, + }; + + return ( + handleEntryError(entry, "tp", { + liqPrice: position.liquidationPrice, + markPrice, + isLong, + isLimit: false, + isExistingPosition: true, + }).price.error ?? undefined + ); + }, [markPrice, tpPriceEntry, sizeUsdEntry, percentageEntry, position.liquidationPrice, isLong]); + + const slPriceError = useMemo(() => { + if (markPrice === undefined) return undefined; + + const entry: SidecarSlTpOrderEntry = { + id: "sl", + price: slPriceEntry, + sizeUsd: sizeUsdEntry, + percentage: percentageEntry, + mode: "keepPercentage", + order: null, + txnType: slPriceEntry.value ? "create" : null, + decreaseAmounts: undefined, + increaseAmounts: undefined, + }; + + return ( + handleEntryError(entry, "sl", { + liqPrice: position.liquidationPrice, + markPrice, + isLong, + isLimit: false, + isExistingPosition: true, + }).price.error ?? undefined + ); + }, [markPrice, slPriceEntry, sizeUsdEntry, percentageEntry, position.liquidationPrice, isLong]); + + const orderPayloads = useMemo((): CreateOrderTxnParams[] => { + if (!account || !marketInfo || !collateralToken) { + return []; + } + + const tpCreateAmounts = tpPriceError ? undefined : tpDecreaseAmounts; + const slCreateAmounts = slPriceError ? undefined : slDecreaseAmounts; + + if (!tpCreateAmounts && !slCreateAmounts) { + return []; + } + + if ((tpCreateAmounts && !tpExecutionFee) || (slCreateAmounts && !slExecutionFee)) { + return []; + } + + const autoCancelOrdersLimitForModal = autoCancelOrdersLimit > 0 ? 2 : 0; + + return buildTpSlCreatePayloads({ + autoCancelOrdersLimit: autoCancelOrdersLimitForModal, + chainId, + account, + marketAddress: marketInfo.marketTokenAddress, + indexTokenAddress: marketInfo.indexTokenAddress, + collateralTokenAddress: collateralToken.address, + isLong, + entries: [ + { + amounts: tpCreateAmounts, + executionFeeAmount: tpExecutionFee?.feeTokenAmount, + executionGasLimit: tpExecutionFee?.gasLimit, + }, + { + amounts: slCreateAmounts, + executionFeeAmount: slExecutionFee?.feeTokenAmount, + executionGasLimit: slExecutionFee?.gasLimit, + }, + ], + userReferralCode: userReferralInfo?.referralCodeForTxn, + }); + }, [ + tpDecreaseAmounts, + slDecreaseAmounts, + account, + marketInfo, + chainId, + collateralToken, + userReferralInfo?.referralCodeForTxn, + autoCancelOrdersLimit, + isLong, + tpPriceError, + slPriceError, + tpExecutionFee, + slExecutionFee, + ]); + + const batchParams = useMemo(() => { + if (orderPayloads.length === 0) return undefined; + return { + createOrderParams: orderPayloads, + updateOrderParams: [], + cancelOrderParams: [], + }; + }, [orderPayloads]); + + const totalExecutionFee = useMemo(() => { + if (!batchParams || !tokensData) return undefined; + + return getBatchTotalExecutionFee({ batchParams, chainId, tokensData }); + }, [batchParams, chainId, tokensData]); + + const { expressParamsPromise } = useExpressOrdersParams({ + orderParams: batchParams, + label: "Add TP/SL", + isGmxAccount: srcChainId !== undefined, + }); + + const submitError = useMemo(() => { + if (!tpPriceInput && !slPriceInput) { + return t`Enter an amount`; + } + if (tpPriceError && tpPriceInput) { + return tpPriceError; + } + if (slPriceError && slPriceInput) { + return slPriceError; + } + if (orderPayloads.length === 0) { + return t`Unable to calculate order`; + } + return undefined; + }, [tpPriceInput, slPriceInput, tpPriceError, slPriceError, orderPayloads.length]); + + const handleSubmit = useCallback(async () => { + if (!signer || !provider || !batchParams || !tokensData || !marketsInfoData) return; + + setIsSubmitting(true); + + try { + const fulfilledExpressParams = await expressParamsPromise; + + await sendBatchOrderTxn({ + chainId, + signer, + batchParams, + expressParams: + fulfilledExpressParams && getIsValidExpressParams(fulfilledExpressParams) + ? fulfilledExpressParams + : undefined, + simulationParams: shouldDisableValidationForTesting + ? undefined + : { + tokensData, + blockTimestampData, + }, + callback: makeOrderTxnCallback({}), + provider, + isGmxAccount: srcChainId !== undefined, + }); + + setIsVisible(false); + onSuccess?.(); + } finally { + setIsSubmitting(false); + } + }, [ + signer, + provider, + batchParams, + chainId, + srcChainId, + expressParamsPromise, + makeOrderTxnCallback, + setIsVisible, + onSuccess, + tokensData, + marketsInfoData, + shouldDisableValidationForTesting, + blockTimestampData, + ]); + + useEffect(() => { + if (!isVisible) { + setTpPriceInput(""); + setSlPriceInput(""); + setCloseSizeInput(""); + setEditTPSLSize(false); + setClosePercentage(100); + setPreviewTab("tp"); + } + }, [isVisible]); + + useEffect(() => { + if (tpDecreaseAmounts && !slDecreaseAmounts) { + setPreviewTab("tp"); + } else if (!tpDecreaseAmounts && slDecreaseAmounts) { + setPreviewTab("sl"); + } + }, [tpDecreaseAmounts, slDecreaseAmounts]); + + const positionTitle = `${position.isLong ? t`Long` : t`Short`} ${indexToken.symbol}`; + + const currentLeverage = formatLeverage(position.leverage); + const nextLeverage = activeNextPositionValues?.nextLeverage; + + const leverageValue: ReactNode = useMemo(() => { + if (activeDecreaseAmounts?.isFullClose) { + return t`NA`; + } + + if (activeDecreaseAmounts?.sizeDeltaUsd === position.sizeInUsd) { + return "-"; + } + + if (activeDecreaseAmounts?.sizeDeltaUsd && activeDecreaseAmounts.sizeDeltaUsd > 0n) { + return ; + } + + return currentLeverage; + }, [ + activeDecreaseAmounts?.isFullClose, + activeDecreaseAmounts?.sizeDeltaUsd, + currentLeverage, + nextLeverage, + position.sizeInUsd, + ]); + + const handleBack = useCallback(() => { + if (onBack) { + onBack(); + return; + } + + setIsVisible(false); + }, [onBack, setIsVisible]); + + return ( + TP/SL: {positionTitle} Decrease} + onBack={onBack ? handleBack : undefined} + withMobileBottomPosition + > +
+
+ +
+ +
+ +
+ +
+ + Keep leverage at {currentLeverage} + + + Edit TP/SL Size + +
+ + {editTPSLSize && ( +
+ { + setCloseSizeInput(formattedMaxCloseSize); + setClosePercentage(100); + }} + showPercentSelector={position.sizeInUsd > 0n} + onPercentChange={handleClosePercentageChange} + qa="close-size" + > + {collateralToken.symbol} + + +
+ )} + + + + {hasPreviewData && ( +
+ + + {activeDecreaseAmounts && ( +
+ Receive} + value={ + activeReceiveDisplay ? ( + + {activeReceiveDisplay.text}{" "} + ({activeReceiveDisplay.usd}) + + ) : ( + "-" + ) + } + /> + Liquidation Price} + value={ + 0n + ? formatLiquidationPrice(activeNextPositionValues?.nextLiqPrice, { + displayDecimals: priceDecimals, + visualMultiplier, + }) + : undefined + } + /> + } + /> + PnL} + value={ + + } + /> + Net Price Impact / Fees} + value={ + + {activeFees ? formatDeltaUsd(activeNetPriceImpactUsd ?? 0n) : "-"} + + } + /> +
+ )} +
+ )} + + + + + + Leverage} value={leverageValue} /> + Size} + value={ + + } + /> + Collateral ({collateralToken.symbol})} + content={Initial collateral (collateral excluding borrow and funding fee).} + variant="icon" + /> + } + value={ + + } + /> + +
+
+ ); +} diff --git a/src/components/TPSLModal/TPSLInputRow.tsx b/src/components/TPSLModal/TPSLInputRow.tsx new file mode 100644 index 0000000000..ff65e0fb97 --- /dev/null +++ b/src/components/TPSLModal/TPSLInputRow.tsx @@ -0,0 +1,533 @@ +import { FloatingPortal, autoUpdate, flip, offset, shift, useFloating } from "@floating-ui/react"; +import { Popover } from "@headlessui/react"; +import { Trans, t } from "@lingui/macro"; +import cx from "classnames"; +import { ChangeEvent, RefObject, useCallback, useEffect, useMemo, useRef, useState } from "react"; + +import { USD_DECIMALS } from "config/factors"; +import { + calculateDisplayDecimals, + expandDecimals, + formatAmount, + formatDeltaUsd, + parseValue, + removeTrailingZeros, +} from "lib/numbers"; +import { bigMath } from "sdk/utils/bigmath"; + +import NumberInput from "components/NumberInput/NumberInput"; +import TooltipWithPortal from "components/Tooltip/TooltipWithPortal"; + +import ChevronDownIcon from "img/ic_chevron_down.svg?react"; + +export type TPSLDisplayMode = "percentage" | "usd"; + +type PositionData = { + sizeInTokens: bigint; + collateralUsd: bigint; + entryPrice: bigint; + referencePrice?: bigint; + liquidationPrice?: bigint; + isLong: boolean; + indexTokenDecimals: number; + visualMultiplier?: number; +}; + +type Props = { + type: "takeProfit" | "stopLoss"; + priceValue: string; + onPriceChange: (value: string) => void; + positionData: PositionData; + priceError?: string; + variant?: "compact" | "full"; + defaultDisplayMode?: TPSLDisplayMode; + estimatedPnl?: { + pnlUsd: bigint; + pnlPercentage?: bigint; + }; +}; + +export function TPSLInputRow({ + type, + priceValue, + onPriceChange, + positionData, + priceError, + variant = "compact", + defaultDisplayMode = "percentage", + estimatedPnl: estimatedPnlProp, +}: Props) { + const priceInputRef = useRef(null); + const secondInputRef = useRef(null); + const displayModePopoverReferenceRef = useRef(null); + const [displayMode, setDisplayMode] = useState(defaultDisplayMode); + const [lastEditedField, setLastEditedField] = useState<"price" | "gainLoss" | undefined>(undefined); + const [gainLossInputValue, setGainLossInputValue] = useState(""); + + const { + sizeInTokens, + collateralUsd, + entryPrice, + referencePrice, + liquidationPrice, + isLong, + indexTokenDecimals, + visualMultiplier = 1, + } = positionData; + const priceReference = referencePrice ?? entryPrice; + const tokenPrecision = expandDecimals(1, indexTokenDecimals); + + const isStopLoss = type === "stopLoss"; + const priceLabel = isStopLoss ? t`Stop Loss Price` : t`Take Profit Price`; + const pricePlaceholder = isStopLoss ? t`SL Price` : t`TP Price`; + const secondLabel = isStopLoss ? t`Loss` : t`Gain`; + + const effectiveLiquidationPrice = useMemo(() => { + if (liquidationPrice === undefined || liquidationPrice === 0n) return undefined; + if (entryPrice === 0n) return undefined; + const priceRange = isLong ? entryPrice - liquidationPrice : liquidationPrice - entryPrice; + if (priceRange <= 0n) return undefined; + const buffer = bigMath.mulDiv(priceRange, 100n, 10000n); + if (isLong) { + return liquidationPrice + buffer; + } else { + return liquidationPrice - buffer; + } + }, [liquidationPrice, entryPrice, isLong]); + + const currentPriceValue = useMemo(() => { + if (!priceValue) return undefined; + return parseValue(priceValue, USD_DECIMALS); + }, [priceValue]); + + const calculatePriceFromPnlUsd = useCallback( + (targetPnlUsd: bigint): bigint | undefined => { + if (sizeInTokens === 0n || priceReference === 0n) return undefined; + + const absPnl = bigMath.abs(targetPnlUsd); + const positionValueUsd = bigMath.mulDiv(priceReference, sizeInTokens, tokenPrecision); + if (positionValueUsd === 0n) return undefined; + + const targetPositionValueUsd = isLong + ? isStopLoss + ? positionValueUsd - absPnl + : positionValueUsd + absPnl + : isStopLoss + ? positionValueUsd + absPnl + : positionValueUsd - absPnl; + + if (targetPositionValueUsd <= 0n) return undefined; + + return bigMath.mulDiv(targetPositionValueUsd, tokenPrecision, sizeInTokens); + }, + [isLong, isStopLoss, priceReference, sizeInTokens, tokenPrecision] + ); + + const calculatePriceFromPnlPercentage = useCallback( + (targetPnlPercentage: bigint): bigint | undefined => { + if (collateralUsd === 0n) return undefined; + const targetPnlUsd = bigMath.mulDiv(collateralUsd, targetPnlPercentage, 10000n); + const price = calculatePriceFromPnlUsd(targetPnlUsd); + if (price === undefined) return undefined; + + if (isStopLoss && effectiveLiquidationPrice !== undefined) { + const isBeyondLiquidation = isLong ? price <= effectiveLiquidationPrice : price >= effectiveLiquidationPrice; + if (isBeyondLiquidation) return effectiveLiquidationPrice; + } + + return price; + }, + [collateralUsd, calculatePriceFromPnlUsd, isStopLoss, effectiveLiquidationPrice, isLong] + ); + + const formatPrice = useCallback( + (price: bigint): string => { + const displayDecimals = calculateDisplayDecimals(price, USD_DECIMALS, visualMultiplier); + return String( + removeTrailingZeros(formatAmount(price, USD_DECIMALS, displayDecimals, undefined, undefined, visualMultiplier)) + ); + }, + [visualMultiplier] + ); + + const formatGainLossValue = useCallback( + (mode: TPSLDisplayMode): string => { + if (currentPriceValue === undefined || currentPriceValue === 0n) return ""; + if (sizeInTokens === 0n || priceReference === 0n) return ""; + + if (mode === "percentage") { + if (collateralUsd > 0n) { + const priceDiff = isLong ? currentPriceValue - priceReference : priceReference - currentPriceValue; + const pnlUsd = bigMath.mulDiv(priceDiff, sizeInTokens, tokenPrecision); + const percentage = bigMath.mulDiv(bigMath.abs(pnlUsd), 10000n, collateralUsd); + return String(removeTrailingZeros(formatAmount(percentage, 2, 2))); + } + } else { + const priceDiff = isLong ? currentPriceValue - priceReference : priceReference - currentPriceValue; + const pnlUsd = bigMath.mulDiv(priceDiff, sizeInTokens, tokenPrecision); + return String(removeTrailingZeros(formatAmount(bigMath.abs(pnlUsd), USD_DECIMALS, 2))); + } + return ""; + }, + [currentPriceValue, collateralUsd, isLong, priceReference, sizeInTokens, tokenPrecision] + ); + + const calculateAndUpdatePrice = useCallback( + (value: string, mode: TPSLDisplayMode) => { + const decimals = mode === "percentage" ? 2 : USD_DECIMALS; + const parsed = parseValue(value, decimals); + if (parsed === undefined || parsed <= 0n) return; + + const calculateFn = mode === "percentage" ? calculatePriceFromPnlPercentage : calculatePriceFromPnlUsd; + const price = calculateFn(parsed); + if (price !== undefined && price > 0n) { + onPriceChange(formatPrice(price)); + } + }, + [calculatePriceFromPnlPercentage, calculatePriceFromPnlUsd, formatPrice, onPriceChange] + ); + + useEffect(() => { + if (lastEditedField !== "gainLoss" || !gainLossInputValue) return; + calculateAndUpdatePrice(gainLossInputValue, displayMode); + }, [lastEditedField, gainLossInputValue, displayMode, calculateAndUpdatePrice]); + + const handlePriceChange = useCallback( + (e: ChangeEvent) => { + setLastEditedField("price"); + setGainLossInputValue(""); + onPriceChange(e.target.value); + }, + [onPriceChange] + ); + + const handleGainLossChange = useCallback( + (e: ChangeEvent) => { + const value = e.target.value; + setLastEditedField("gainLoss"); + setGainLossInputValue(value); + calculateAndUpdatePrice(value, displayMode); + }, + [displayMode, calculateAndUpdatePrice] + ); + + const secondFieldValue = useMemo(() => { + if (lastEditedField === "gainLoss") { + return gainLossInputValue; + } + return formatGainLossValue(displayMode); + }, [lastEditedField, gainLossInputValue, formatGainLossValue, displayMode]); + + const derivedEstimatedPnl = useMemo(() => { + if (currentPriceValue === undefined || currentPriceValue === 0n) return undefined; + if (priceReference === 0n || sizeInTokens === 0n) return undefined; + + const priceDiff = isLong ? currentPriceValue - priceReference : priceReference - currentPriceValue; + const pnlUsd = bigMath.mulDiv(priceDiff, sizeInTokens, tokenPrecision); + const pnlPercentage = collateralUsd > 0n ? bigMath.mulDiv(pnlUsd, 10000n, collateralUsd) : undefined; + + return { + pnlUsd, + pnlPercentage, + }; + }, [collateralUsd, currentPriceValue, isLong, priceReference, sizeInTokens, tokenPrecision]); + + const convertGainLossValue = useCallback( + (value: string, fromMode: TPSLDisplayMode, toMode: TPSLDisplayMode): string => { + if (collateralUsd === 0n) return ""; + + if (fromMode === "percentage" && toMode === "usd") { + const parsed = parseValue(value, 2); + if (parsed === undefined || parsed <= 0n) return ""; + const targetPnlUsd = bigMath.mulDiv(collateralUsd, parsed, 10000n); + return String(removeTrailingZeros(formatAmount(bigMath.abs(targetPnlUsd), USD_DECIMALS, 2))); + } + + if (fromMode === "usd" && toMode === "percentage") { + const parsed = parseValue(value, USD_DECIMALS); + if (parsed === undefined || parsed <= 0n) return ""; + const percentage = bigMath.mulDiv(parsed, 10000n, collateralUsd); + return String(removeTrailingZeros(formatAmount(percentage, 2, 2))); + } + + return value; + }, + [collateralUsd] + ); + + const handleDisplayModeChange = useCallback( + (mode: TPSLDisplayMode) => { + if (mode === displayMode) return; + + const prevMode = displayMode; + setDisplayMode(mode); + + if (lastEditedField === "gainLoss" && gainLossInputValue) { + setGainLossInputValue(convertGainLossValue(gainLossInputValue, prevMode, mode)); + } + }, + [displayMode, lastEditedField, gainLossInputValue, convertGainLossValue] + ); + + const handleBoxClick = (ref: React.RefObject) => (e: React.MouseEvent) => { + if (!(e.target as HTMLElement).closest("[data-dropdown]")) { + ref.current?.focus(); + } + }; + + const estimatedPnl = estimatedPnlProp ?? derivedEstimatedPnl; + const estimatedPnlDisplay = estimatedPnl ? formatDeltaUsd(estimatedPnl.pnlUsd, estimatedPnl.pnlPercentage) : "-"; + const estimatedPnlRow = ( +
+ + Est. PnL + + 0n, + "text-red-500": estimatedPnl && estimatedPnl.pnlUsd < 0n, + })} + > + {estimatedPnlDisplay ?? "-"} + +
+ ); + + if (variant === "compact") { + return ( +
+
+ +
+
+
+ +
+ USD +
+
+
+ +
+
+
+ +
+ +
+
+
+ {estimatedPnlRow} +
+ ); + } + + return ( +
+
+ +
+
{priceLabel}
+
+ + USD +
+
+
+
+
+ {secondLabel} +
+
+ + +
+
+
+ {estimatedPnlRow} +
+ ); +} + +function DisplayModeSelector({ + mode, + setMode, + size = "normal", + popoverReferenceRef, +}: { + mode: TPSLDisplayMode; + setMode: (mode: TPSLDisplayMode) => void; + size?: "small" | "normal"; + popoverReferenceRef?: RefObject; +}) { + const buttonRef = useRef(null); + + const { refs, floatingStyles } = useFloating({ + middleware: [offset(4), flip(), shift()], + placement: "bottom-end", + whileElementsMounted: autoUpdate, + elements: { + reference: popoverReferenceRef?.current ?? buttonRef.current, + }, + }); + + const setButtonRef = useCallback( + (node: HTMLElement | null) => { + buttonRef.current = node; + + if (!popoverReferenceRef?.current) { + refs.setReference(node); + } + }, + [popoverReferenceRef, refs] + ); + + const setPopoverButtonRef = useCallback( + (node: HTMLButtonElement | null) => { + setButtonRef(node); + }, + [setButtonRef] + ); + + const popoverPanelStyle = useMemo( + () => ({ ...floatingStyles, zIndex: "calc(var(--modal-z-index) + 1)" }), + [floatingStyles] + ); + + return ( + + {({ open }) => ( + <> + + {mode === "percentage" ? "%" : "$"} + + + {open && ( + + + {({ close }) => ( + <> + + + + )} + + + )} + + )} + + ); +} diff --git a/src/components/TPSLModal/TPSLModal.tsx b/src/components/TPSLModal/TPSLModal.tsx new file mode 100644 index 0000000000..91141dacaf --- /dev/null +++ b/src/components/TPSLModal/TPSLModal.tsx @@ -0,0 +1,342 @@ +import { Trans, t } from "@lingui/macro"; +import cx from "classnames"; +import { useCallback, useEffect, useMemo, useState } from "react"; + +import { usePositionsConstants } from "context/SyntheticsStateContext/hooks/globalsHooks"; +import { + useCancellingOrdersKeysState, + useEditingOrderState, +} from "context/SyntheticsStateContext/hooks/orderEditorHooks"; +import { usePositionOrdersWithErrors } from "context/SyntheticsStateContext/hooks/orderHooks"; +import { selectExpressGlobalParams } from "context/SyntheticsStateContext/selectors/expressSelectors"; +import { + selectChainId, + selectSrcChainId, + selectSubaccountForChainAction, +} from "context/SyntheticsStateContext/selectors/globalSelectors"; +import { makeSelectMarketPriceDecimals } from "context/SyntheticsStateContext/selectors/statsSelectors"; +import { useSelector } from "context/SyntheticsStateContext/utils"; +import { estimateBatchExpressParams } from "domain/synthetics/express/expressOrderUtils"; +import { + isLimitDecreaseOrderType, + isStopLossOrderType, + isTwapOrder, + PositionOrderInfo, +} from "domain/synthetics/orders"; +import { sendBatchOrderTxn } from "domain/synthetics/orders/sendBatchOrderTxn"; +import { useOrderTxnCallbacks } from "domain/synthetics/orders/useOrderTxnCallbacks"; +import { PositionInfo, formatLiquidationPrice, getEstimatedLiquidationTimeInHours } from "domain/synthetics/positions"; +import { formatUsd } from "lib/numbers"; +import { useJsonRpcProvider } from "lib/rpc"; +import { useBreakpoints } from "lib/useBreakpoints"; +import { useEthersSigner } from "lib/wallets/useEthersSigner"; +import { getMarketIndexName } from "sdk/utils/markets"; +import { getOrderKeys } from "sdk/utils/orders"; + +import Badge from "components/Badge/Badge"; +import Button from "components/Button/Button"; +import Modal from "components/Modal/Modal"; +import Tabs from "components/Tabs/Tabs"; + +import PlusIcon from "img/ic_buy.svg?react"; + +import { AddTPSLModal } from "./AddTPSLModal"; +import { TPSLOrdersList } from "./TPSLOrdersList"; + +type TabType = "all" | "takeProfit" | "stopLoss"; + +type Props = { + isVisible: boolean; + setIsVisible: (visible: boolean) => void; + position: PositionInfo; +}; + +export function TPSLModal({ isVisible, setIsVisible, position }: Props) { + const [activeTab, setActiveTab] = useState("all"); + const [isCancellingAll, setIsCancellingAll] = useState(false); + const [isAddFormVisible, setIsAddFormVisible] = useState(false); + const [shouldReopenOnAddClose, setShouldReopenOnAddClose] = useState(false); + const [shouldReopenOnEditClose, setShouldReopenOnEditClose] = useState(false); + + const { isTablet } = useBreakpoints(); + const isMobile = isTablet; + + const chainId = useSelector(selectChainId); + const srcChainId = useSelector(selectSrcChainId); + const signer = useEthersSigner(); + const { provider } = useJsonRpcProvider(chainId); + const [, setCancellingOrdersKeys] = useCancellingOrdersKeysState(); + const [editingOrderState, setEditingOrderState] = useEditingOrderState(); + const { makeOrderTxnCallback } = useOrderTxnCallbacks(); + const globalExpressParams = useSelector(selectExpressGlobalParams); + const subaccount = useSelector(selectSubaccountForChainAction); + + const ordersWithErrors = usePositionOrdersWithErrors(position.key); + const marketDecimals = useSelector(makeSelectMarketPriceDecimals(position.market.indexTokenAddress)); + const { minCollateralUsd } = usePositionsConstants(); + const estimatedLiquidationHours = useMemo( + () => getEstimatedLiquidationTimeInHours(position, minCollateralUsd), + [position, minCollateralUsd] + ); + + const { tpOrders, slOrders, allOrders } = useMemo(() => { + const tpOrders: PositionOrderInfo[] = []; + const slOrders: PositionOrderInfo[] = []; + + for (const { order } of ordersWithErrors) { + if (isTwapOrder(order)) continue; + if (isLimitDecreaseOrderType(order.orderType)) { + tpOrders.push(order); + } else if (isStopLossOrderType(order.orderType)) { + slOrders.push(order); + } + } + + return { + tpOrders, + slOrders, + allOrders: [...tpOrders, ...slOrders], + }; + }, [ordersWithErrors]); + + const displayedOrders = useMemo(() => { + switch (activeTab) { + case "takeProfit": + return tpOrders; + case "stopLoss": + return slOrders; + default: + return allOrders; + } + }, [activeTab, tpOrders, slOrders, allOrders]); + + const tabOptions = useMemo( + () => [ + { value: "all" as TabType, label: t`All` }, + { + value: "takeProfit" as TabType, + label: ( + <> + Take Profit {tpOrders.length > 0 ? {tpOrders.length} : null} + + ), + }, + { + value: "stopLoss" as TabType, + label: ( + <> + Stop Loss {slOrders.length > 0 ? {slOrders.length} : null} + + ), + }, + ], + [tpOrders.length, slOrders.length] + ); + + const handleCancelAll = useCallback(async () => { + if (!signer || !provider || displayedOrders.length === 0) return; + + setIsCancellingAll(true); + + const orderKeys = displayedOrders.flatMap((order) => getOrderKeys(order)); + setCancellingOrdersKeys((prev) => [...new Set([...prev, ...orderKeys])]); + + const batchParams = { + createOrderParams: [], + updateOrderParams: [], + cancelOrderParams: orderKeys.map((k) => ({ orderKey: k })), + }; + + try { + const expressParams = globalExpressParams + ? await estimateBatchExpressParams({ + signer, + chainId, + batchParams, + globalExpressParams, + requireValidations: true, + estimationMethod: "approximate", + provider, + isGmxAccount: srcChainId !== undefined, + subaccount, + }) + : undefined; + + await sendBatchOrderTxn({ + chainId, + signer, + batchParams, + expressParams, + simulationParams: undefined, + callback: makeOrderTxnCallback({}), + provider, + isGmxAccount: srcChainId !== undefined, + }); + } finally { + setIsCancellingAll(false); + setCancellingOrdersKeys((prev) => prev.filter((k) => !orderKeys.includes(k))); + } + }, [ + signer, + provider, + displayedOrders, + chainId, + srcChainId, + globalExpressParams, + subaccount, + makeOrderTxnCallback, + setCancellingOrdersKeys, + ]); + + const handleAddTPSLOpen = useCallback(() => { + setShouldReopenOnAddClose(false); + setIsVisible(false); + setIsAddFormVisible(true); + }, [setIsAddFormVisible, setIsVisible, setShouldReopenOnAddClose]); + + const handleAddTPSLBack = useCallback(() => { + setShouldReopenOnAddClose(true); + setIsAddFormVisible(false); + }, [setIsAddFormVisible, setShouldReopenOnAddClose]); + + const handleAddTPSLSuccess = useCallback(() => { + setShouldReopenOnAddClose(true); + }, [setShouldReopenOnAddClose]); + + const handleEditOrder = useCallback( + (orderKey: string) => { + setIsVisible(false); + setShouldReopenOnEditClose(true); + setEditingOrderState({ orderKey, source: "TPSLModal" }); + }, + [setEditingOrderState, setIsVisible] + ); + + useEffect(() => { + if (!isAddFormVisible && shouldReopenOnAddClose) { + setIsVisible(true); + setShouldReopenOnAddClose(false); + } + }, [isAddFormVisible, shouldReopenOnAddClose, setIsVisible]); + + useEffect(() => { + if (shouldReopenOnEditClose && editingOrderState?.source === "TPSLModal" && !editingOrderState.orderKey) { + setIsVisible(true); + setEditingOrderState(undefined); + setShouldReopenOnEditClose(false); + } + }, [editingOrderState, setEditingOrderState, setIsVisible, shouldReopenOnEditClose]); + + useEffect(() => { + if (!shouldReopenOnEditClose) return; + if (!editingOrderState || editingOrderState.source !== "TPSLModal") { + setShouldReopenOnEditClose(false); + } + }, [editingOrderState, shouldReopenOnEditClose]); + + const positionTitle = `${position.isLong ? t`Long` : t`Short`} ${getMarketIndexName({ indexToken: position.indexToken, isSpotOnly: false })} `; + + return ( + <> + TP/SL: {positionTitle}} + className="max-lg:!w-full max-lg:!items-end" + contentClassName="!max-w-[896px] w-[95%] h-[min(90vh,500px)] max-lg:h-[85vh] max-lg:!w-full max-lg:!max-w-none" + contentPadding={false} + withMobileBottomPosition={true} + > +
+
+ + Entry Price + + + {formatUsd(position.entryPrice, { + displayDecimals: marketDecimals, + visualMultiplier: position.indexToken.visualMultiplier, + })} + +
+
+ + Mark Price + + + {formatUsd(position.markPrice, { + displayDecimals: marketDecimals, + visualMultiplier: position.indexToken.visualMultiplier, + })} + +
+
+ + Liquidation Price + + + {formatLiquidationPrice(position.liquidationPrice, { + displayDecimals: marketDecimals, + visualMultiplier: position.indexToken.visualMultiplier, + }) || "..."} + +
+
+ +
+ + {displayedOrders.length > 0 && ( + + )} + {!isMobile && ( + + )} +
+ } + /> +
+ + + + {isMobile && ( +
+ +
+ )} + + + + + ); +} diff --git a/src/components/TPSLModal/TPSLOrdersList.tsx b/src/components/TPSLModal/TPSLOrdersList.tsx new file mode 100644 index 0000000000..3fb4520e4a --- /dev/null +++ b/src/components/TPSLModal/TPSLOrdersList.tsx @@ -0,0 +1,387 @@ +import { Trans, t } from "@lingui/macro"; +import cx from "classnames"; +import { useCallback, useMemo } from "react"; + +import { + usePositionsConstants, + useUiFeeFactor, + useUserReferralInfo, +} from "context/SyntheticsStateContext/hooks/globalsHooks"; +import { useEditingOrderState } from "context/SyntheticsStateContext/hooks/orderEditorHooks"; +import { useCancelOrder } from "context/SyntheticsStateContext/hooks/orderHooks"; +import { selectIsSetAcceptablePriceImpactEnabled } from "context/SyntheticsStateContext/selectors/settingsSelectors"; +import { useSelector } from "context/SyntheticsStateContext/utils"; +import { isLimitDecreaseOrderType, OrderType, PositionOrderInfo } from "domain/synthetics/orders"; +import { PositionInfo, getIsPositionInfoLoaded } from "domain/synthetics/positions"; +import { getDecreasePositionAmounts } from "domain/synthetics/trade"; +import { formatDeltaUsd, formatUsd, formatBalanceAmount, formatPercentage } from "lib/numbers"; +import { getPositiveOrNegativeClass } from "lib/utils"; +import { bigMath } from "sdk/utils/bigmath"; + +import Button from "components/Button/Button"; +import { Table, TableTh, TableTheadTr } from "components/Table/Table"; +import { TableTd, TableTr } from "components/Table/Table"; + +import CloseIcon from "img/ic_close.svg?react"; +import EditIcon from "img/ic_edit.svg?react"; + +type Props = { + orders: PositionOrderInfo[]; + position: PositionInfo; + marketDecimals: number | undefined; + isMobile: boolean; + onEdit?: (orderKey: string) => void; +}; + +export function TPSLOrdersList({ orders, position, marketDecimals, isMobile, onEdit }: Props) { + const [, setEditingOrderState] = useEditingOrderState(); + + const handleEditOrder = useCallback( + (orderKey: string) => { + if (onEdit) { + onEdit(orderKey); + return; + } + setEditingOrderState({ orderKey, source: "PositionsList" }); + }, + [onEdit, setEditingOrderState] + ); + + if (orders.length === 0) { + return ( +
+ No TP/SL orders +
+ ); + } + + if (isMobile) { + return ( +
+ {orders.map((order) => ( + + ))} +
+ ); + } + + return ( +
+ + + + Type + + + Size (% of Position) + + + Trigger Price + + + Est. PnL + + + Receive + + + + + + {orders.map((order) => ( + + ))} + +
+ ); +} + +function useTPSLOrderViewModel({ + order, + position, + marketDecimals, + onEdit, +}: { + order: PositionOrderInfo; + position: PositionInfo; + marketDecimals: number | undefined; + onEdit?: (orderKey: string) => void; +}) { + const { minCollateralUsd, minPositionSizeUsd } = usePositionsConstants(); + const uiFeeFactor = useUiFeeFactor(); + const userReferralInfo = useUserReferralInfo(); + const isSetAcceptablePriceImpactEnabled = useSelector(selectIsSetAcceptablePriceImpactEnabled); + const [isCancelling, cancelOrder] = useCancelOrder(order); + + const orderType = useMemo( + () => (isLimitDecreaseOrderType(order.orderType) ? t`Take Profit` : t`Stop Loss`), + [order.orderType] + ); + + const triggerPriceDisplay = useMemo( + () => + `${order.triggerThresholdType} ${formatUsd(order.triggerPrice, { + displayDecimals: marketDecimals, + visualMultiplier: order.indexToken?.visualMultiplier, + })}`, + [marketDecimals, order.indexToken?.visualMultiplier, order.triggerPrice, order.triggerThresholdType] + ); + + const sizeDisplay = useMemo(() => { + const isFullClose = order.sizeDeltaUsd === position.sizeInUsd; + + if (isFullClose) { + return Full Position Close; + } + + const sizePercentage = + position.sizeInUsd === 0n ? 0n : bigMath.mulDiv(order.sizeDeltaUsd, 10000n, position.sizeInUsd); + + return ( + + -{formatUsd(order.sizeDeltaUsd)} + (-{formatPercentage(sizePercentage)}) + + ); + }, [order.sizeDeltaUsd, position.sizeInUsd]); + + const estimatedPnl = useMemo(() => { + const entryPrice = position.entryPrice ?? 0n; + const priceDiff = order.isLong ? order.triggerPrice - entryPrice : entryPrice - order.triggerPrice; + + const pnlUsd = entryPrice > 0n ? bigMath.mulDiv(priceDiff, order.sizeDeltaUsd, entryPrice) : 0n; + const pnlPercentage = position.collateralUsd > 0n ? bigMath.mulDiv(pnlUsd, 10000n, position.collateralUsd) : 0n; + + return { pnlUsd, pnlPercentage }; + }, [order.isLong, order.sizeDeltaUsd, order.triggerPrice, position.collateralUsd, position.entryPrice]); + + const shouldKeepLeverage = useMemo(() => { + if (order.sizeDeltaUsd >= position.sizeInUsd) { + return true; + } + + return order.initialCollateralDeltaAmount > 0n; + }, [order.initialCollateralDeltaAmount, order.sizeDeltaUsd, position.sizeInUsd]); + + const decreaseAmounts = useMemo(() => { + if (minCollateralUsd === undefined || minPositionSizeUsd === undefined || !getIsPositionInfoLoaded(position)) { + return undefined; + } + + return getDecreasePositionAmounts({ + marketInfo: order.marketInfo, + collateralToken: position.collateralToken, + receiveToken: order.targetCollateralToken, + isLong: order.isLong, + position, + closeSizeUsd: order.sizeDeltaUsd, + keepLeverage: shouldKeepLeverage, + triggerPrice: order.triggerPrice, + userReferralInfo, + minCollateralUsd, + minPositionSizeUsd, + uiFeeFactor, + triggerOrderType: order.orderType as OrderType.LimitDecrease | OrderType.StopLossDecrease, + isSetAcceptablePriceImpactEnabled, + }); + }, [ + minCollateralUsd, + minPositionSizeUsd, + order.marketInfo, + order.targetCollateralToken, + order.isLong, + order.orderType, + order.sizeDeltaUsd, + order.triggerPrice, + position, + shouldKeepLeverage, + userReferralInfo, + uiFeeFactor, + isSetAcceptablePriceImpactEnabled, + ]); + + const receiveDisplay = useMemo(() => { + if (!decreaseAmounts) return "—"; + + const receiveUsd = decreaseAmounts.receiveUsd; + const receiveToken = order.targetCollateralToken; + + if (!receiveToken) return "—"; + + const receiveAmount = decreaseAmounts.receiveTokenAmount ?? 0n; + + return ( + + {formatBalanceAmount(receiveAmount, receiveToken.decimals, receiveToken.symbol, { + isStable: receiveToken.isStable, + })} + ({formatUsd(receiveUsd)}) + + ); + }, [decreaseAmounts, order.targetCollateralToken]); + + const handleEdit = useCallback(() => { + onEdit?.(order.key); + }, [onEdit, order.key]); + + const handleCancel = useCallback(() => { + cancelOrder(); + }, [cancelOrder]); + + return { + orderType, + triggerPriceDisplay, + sizeDisplay, + estimatedPnl, + receiveDisplay, + handleEdit, + handleCancel, + isCancelling, + }; +} + +function TPSLOrderCard({ + order, + position, + marketDecimals, + onEdit, +}: { + order: PositionOrderInfo; + position: PositionInfo; + marketDecimals: number | undefined; + onEdit?: (orderKey: string) => void; +}) { + const { + orderType, + triggerPriceDisplay, + sizeDisplay, + estimatedPnl, + receiveDisplay, + handleEdit, + handleCancel, + isCancelling, + } = useTPSLOrderViewModel({ order, position, marketDecimals, onEdit }); + + return ( +
+
+ + Type + + {orderType} +
+ +
+ + Size (% of position) + + {sizeDisplay} +
+ +
+ + Trigger Price + + {triggerPriceDisplay} +
+ +
+ + Est. PNL + + + {formatDeltaUsd(estimatedPnl.pnlUsd, estimatedPnl.pnlPercentage)} + +
+ +
+ + Receive + + {receiveDisplay} +
+ +
+ + +
+
+ ); +} + +export function TPSLOrderRow({ + order, + position, + marketDecimals, + onEdit, +}: { + order: PositionOrderInfo; + position: PositionInfo; + marketDecimals: number | undefined; + onEdit?: (orderKey: string) => void; +}) { + const { + orderType, + triggerPriceDisplay, + sizeDisplay, + estimatedPnl, + receiveDisplay, + handleEdit, + handleCancel, + isCancelling, + } = useTPSLOrderViewModel({ order, position, marketDecimals, onEdit }); + + return ( + + + {orderType} + + + {sizeDisplay} + + + {triggerPriceDisplay} + + + + {formatDeltaUsd(estimatedPnl.pnlUsd, estimatedPnl.pnlPercentage)} + + + + {receiveDisplay} + + +
+ + +
+
+
+ ); +} diff --git a/src/components/Tabs/RegularTab.tsx b/src/components/Tabs/RegularTab.tsx index 7ffcd73282..b6c899bba6 100644 --- a/src/components/Tabs/RegularTab.tsx +++ b/src/components/Tabs/RegularTab.tsx @@ -61,11 +61,12 @@ export default function RegularTab({ + {!hideChevron && ( + + )}
(null); const selectedMarket = marketAddress ? getByKey(marketsInfoData, marketAddress) : undefined; const poolName = selectedMarket ? getMarketPoolName(selectedMarket) : undefined; return ( - <> - - - - } - /> - - - + + } + /> ); } diff --git a/src/components/TradeBox/TradeBox.tsx b/src/components/TradeBox/TradeBox.tsx index 0f1cc5e033..1852476b9b 100644 --- a/src/components/TradeBox/TradeBox.tsx +++ b/src/components/TradeBox/TradeBox.tsx @@ -1,10 +1,10 @@ import { t, Trans } from "@lingui/macro"; import { useConnectModal } from "@rainbow-me/rainbowkit"; import cx from "classnames"; -import { ChangeEvent, KeyboardEvent, useCallback, useEffect, useMemo, useRef } from "react"; +import { ChangeEvent, useCallback, useEffect, useMemo, useRef } from "react"; import { useKey, useLatest, usePrevious } from "react-use"; -import { AVALANCHE, GMX_ACCOUNT_PSEUDO_CHAIN_ID } from "config/chains"; +import { GMX_ACCOUNT_PSEUDO_CHAIN_ID } from "config/chains"; import { BASIS_POINTS_DIVISOR, USD_DECIMALS } from "config/factors"; import { isSettlementChain } from "config/multichain"; import { useOpenMultichainDepositModal } from "context/GmxAccountContext/useOpenMultichainDepositModal"; @@ -37,7 +37,6 @@ import { selectTradeboxIsWrapOrUnwrap, selectTradeboxKeepLeverage, selectTradeboxLeverage, - selectTradeboxLeverageSliderMarks, selectTradeboxMarkPrice, selectTradeboxMaxLeverage, selectTradeboxNextPositionValues, @@ -95,9 +94,7 @@ import Button from "components/Button/Button"; import BuyInputSection from "components/BuyInputSection/BuyInputSection"; import { ColorfulBanner } from "components/ColorfulBanner/ColorfulBanner"; import ExternalLink from "components/ExternalLink/ExternalLink"; -import { LeverageSlider } from "components/LeverageSlider/LeverageSlider"; import { MarketSelector } from "components/MarketSelector/MarketSelector"; -import SuggestionInput from "components/SuggestionInput/SuggestionInput"; import { SyntheticsInfoRow } from "components/SyntheticsInfoRow"; import Tabs from "components/Tabs/Tabs"; import ToggleSwitch from "components/ToggleSwitch/ToggleSwitch"; @@ -106,6 +103,8 @@ import TokenWithIcon from "components/TokenIcon/TokenWithIcon"; import { MultichainTokenSelector } from "components/TokenSelector/MultichainTokenSelector"; import TokenSelector from "components/TokenSelector/TokenSelector"; import Tooltip from "components/Tooltip/Tooltip"; +import { TradeboxMarginFields } from "components/TradeboxMarginFields"; +import { TradeboxPoolWarnings } from "components/TradeboxPoolWarnings/TradeboxPoolWarnings"; import { ValueTransition } from "components/ValueTransition/ValueTransition"; import ArrowDownIcon from "img/ic_arrow_down.svg?react"; @@ -120,17 +119,15 @@ import TradeInfoIcon from "../TradeInfoIcon/TradeInfoIcon"; import TwapRows from "../TwapRows/TwapRows"; import { useDecreaseOrdersThatWillBeExecuted } from "./hooks/useDecreaseOrdersThatWillBeExecuted"; import { useShowHighLeverageWarning } from "./hooks/useShowHighLeverageWarning"; -import { useExpressTradingWarnings } from "./hooks/useShowOneClickTradingInfo"; import { useTradeboxAcceptablePriceImpactValues } from "./hooks/useTradeboxAcceptablePriceImpactValues"; import { useTradeboxTPSLReset } from "./hooks/useTradeboxTPSLReset"; import { useTradeboxButtonState } from "./hooks/useTradeButtonState"; -import { MarketPoolSelectorRow } from "./MarketPoolSelectorRow"; import { tradeModeLabels, tradeTypeLabels } from "./tradeboxConstants"; import { TradeBoxAdvancedGroups } from "./TradeBoxRows/AdvancedDisplayRows"; -import { CollateralSelectorRow } from "./TradeBoxRows/CollateralSelectorRow"; -import { LimitAndTPSLGroup } from "./TradeBoxRows/LimitAndTPSLRows"; +import { useCollateralWarnings } from "./TradeBoxRows/CollateralSelectorField"; import { MinReceiveRow } from "./TradeBoxRows/MinReceiveRow"; import { PriceImpactFeesRow } from "./TradeBoxRows/PriceImpactFeesRow"; +import { TPSLGroup } from "./TradeBoxRows/TPSLRows"; import "./TradeBox.scss"; @@ -154,7 +151,7 @@ export function TradeBox({ isMobile }: { isMobile: boolean }) { const { tokenChainDataArray: multichainTokens } = useMultichainTokens(); const marketsInfoData = useSelector(selectMarketsInfoData); const tradeFlags = useSelector(selectTradeboxTradeFlags); - const { isLong, isSwap, isIncrease, isPosition, isLimit, isTrigger, isMarket, isTwap } = tradeFlags; + const { isLong, isSwap, isIncrease, isPosition, isLimit, isTrigger, isTwap } = tradeFlags; const isWrapOrUnwrap = useSelector(selectTradeboxIsWrapOrUnwrap); const chainId = useSelector(selectChainId); @@ -177,7 +174,6 @@ export function TradeBox({ isMobile }: { isMobile: boolean }) { setFromTokenInputValue: setFromTokenInputValueRaw, toTokenInputValue, setToTokenInputValue: setToTokenInputValueRaw, - setCollateralAddress: onSelectCollateralAddress, setFromTokenAddress: onSelectFromTokenAddress, isFromTokenGmxAccount, setIsFromTokenGmxAccount, @@ -190,8 +186,6 @@ export function TradeBox({ isMobile }: { isMobile: boolean }) { setTriggerPriceInputValue, triggerRatioInputValue, setTriggerRatioInputValue, - leverageInputValue, - setLeverageInputValue, leverageOption, setLeverageOption, isSwitchTokensAllowed, @@ -247,7 +241,6 @@ export function TradeBox({ isMobile }: { isMobile: boolean }) { const executionFee = useSelector(selectTradeboxExecutionFee); const { markRatio } = useSelector(selectTradeboxTradeRatios); - const leverageSliderMarks = useSelector(selectTradeboxLeverageSliderMarks); const maxLeverage = useSelector(selectTradeboxMaxLeverage); const maxAllowedLeverage = maxLeverage / 2; @@ -379,6 +372,7 @@ export function TradeBox({ isMobile }: { isMobile: boolean }) { nativeToken, fromTokenAmount, fromTokenInputValue, + tokenBalanceType: fromToken?.balanceType, minResidualAmount: getMinResidualGasPaymentTokenAmount({ gasPaymentToken: gasPaymentTokenData, gasPaymentTokenAmount: gasPaymentTokenAmountForMax, @@ -711,35 +705,6 @@ export function TradeBox({ isMobile }: { isMobile: boolean }) { [isCursorInside, wrappedOnSubmit, submitButtonState, shouldDisableValidation] ); - const handleLeverageInputBlur = useCallback(() => { - if (leverageOption === 0) { - setLeverageOption(leverageSliderMarks[0]); - return; - } - - if (leverageInputValue === "" && leverageOption !== undefined) { - setLeverageInputValue(leverageOption.toString()); - } - }, [leverageInputValue, leverageOption, leverageSliderMarks, setLeverageInputValue, setLeverageOption]); - - const handleLeverageInputKeyDown = useCallback( - (e: KeyboardEvent) => { - if (e.key === "ArrowUp" || e.key === "ArrowDown") { - e.preventDefault(); - - const isAlt = e.altKey; - const direction = e.key === "ArrowUp" ? 1 : -1; - const increment = isAlt ? 0.1 : 1; - const diff = direction * increment; - const newValue = Math.round(((leverageOption ?? leverageSliderMarks[0]) + diff) * 10) / 10; - const clampedValue = Math.min(Math.max(newValue, leverageSliderMarks[0]), leverageSliderMarks.at(-1)!); - - setLeverageOption(clampedValue); - } - }, - [leverageOption, leverageSliderMarks, setLeverageOption] - ); - const payUsd = isIncrease ? increaseAmounts?.initialCollateralUsd : fromUsd; function renderTokenInputs() { @@ -1031,25 +996,6 @@ export function TradeBox({ isMobile }: { isMobile: boolean }) { const setKeepLeverage = useSelector(selectTradeboxSetKeepLeverage); const settingsWarningDotVisible = useSelector(selectSettingsWarningDotVisible); - const { shouldShowWarning: shouldShowOneClickTradingWarning } = useExpressTradingWarnings({ - expressParams: submitButtonState.expressParams, - payTokenAddress: fromTokenAddress, - isWrapOrUnwrap, - isGmxAccount: isFromTokenGmxAccount, - }); - - const shouldShowAvalancheGmxAccountWarning = isFromTokenGmxAccount && chainId === AVALANCHE; - - const showSectionBetweenInputsAndButton = - isPosition || - priceImpactWarningState.shouldShowWarning || - (!isTrigger && !isSwap) || - (isSwap && isLimit) || - isTwap || - maxAutoCancelOrdersWarning || - shouldShowOneClickTradingWarning || - shouldShowAvalancheGmxAccountWarning; - const tabsOptions = useMemo(() => { const modeToOptions = (mode: TradeMode) => ({ value: mode, @@ -1066,9 +1012,11 @@ export function TradeBox({ isMobile }: { isMobile: boolean }) { ); }, [availableTradeModes, localizedTradeModeLabels]); + const collateralWarnings = useCollateralWarnings(); + return (
-
+
- {(isSwap || isIncrease) && renderTokenInputs()} + {isSwap && renderTokenInputs()} + {isIncrease && ( + + )} {isTrigger && renderDecreaseSizeInput()} {isSwap && isLimit && renderTriggerRatioInput()} - {isPosition && (isLimit || isTrigger) && renderTriggerPriceInput()} + {isTrigger && renderTriggerPriceInput()}
{twapRecommendation && ( @@ -1118,137 +1083,99 @@ export function TradeBox({ isMobile }: { isMobile: boolean }) { )} - {showSectionBetweenInputsAndButton && ( -
- {maxAutoCancelOrdersWarning} - {shouldShowAvalancheGmxAccountWarning && ( - - - Support for GMX accounts on Avalanche will be discontinued soon. Opening new positions from and - depositing additional funds to Avalanche GMX accounts is no longer available. We recommend - switching to Arbitrum as a settlement network. - - - )} - {isSwap && isLimit && !isTwap && !limitPriceWarningHidden && ( - setLimitPriceWarningHidden(true)}> - - The execution price may vary from your set limit price due to fees and price impact, ensuring you - receive the displayed minimum receive amount.{" "} - - Read more - - . - - - )} - - {isPosition && ( - <> - {isIncrease && isLeverageSliderEnabled && ( -
- - -
- )} - {showHighLeverageWarning && ( - - Using high leverage increases the risk of liquidation. - - )} - {isTrigger && ( - - } - /> - )} - - - - - - )} + {maxAutoCancelOrdersWarning} + {isSwap && isLimit && !isTwap && !limitPriceWarningHidden && ( + setLimitPriceWarningHidden(true)}> + + The execution price may vary from your set limit price due to fees and price impact, ensuring you + receive the displayed minimum receive amount.{" "} + + Read more + + . + + + )} - {isTwap && ( - + Using high leverage increases the risk of liquidation. + + )} + {isTrigger && ( + - )} -
+ } + /> )} -
- {isPosition && isTrigger && selectedPosition && selectedPosition?.leverage !== undefined && ( - - - Keep leverage at {formatLeverage(selectedPosition.leverage)} - - - )} - - {!isTrigger && !isSwap && !isTwap && } - {priceImpactWarningState.shouldShowWarning && ( - - )} + {isPosition ? ( + <> + -
{button}
- + ) : null} + + {isTwap && ( + -
+ )}
+
+ {isPosition && isTrigger && selectedPosition && selectedPosition?.leverage !== undefined && ( + + + Keep leverage at {formatLeverage(selectedPosition.leverage)} + + + )} + + {!isTrigger && !isSwap && !isTwap && } + + {priceImpactWarningState.shouldShowWarning && ( + + )} + +
{button}
+ +
diff --git a/src/components/TradeBox/TradeBoxHeaderTabs.tsx b/src/components/TradeBox/TradeBoxHeaderTabs.tsx index 4e7a62e663..f3993958f6 100644 --- a/src/components/TradeBox/TradeBoxHeaderTabs.tsx +++ b/src/components/TradeBox/TradeBoxHeaderTabs.tsx @@ -1,22 +1,41 @@ +import cx from "classnames"; import { useCallback, useMemo } from "react"; import { useHistory } from "react-router-dom"; +import { selectIsLeverageSliderEnabled } from "context/SyntheticsStateContext/selectors/settingsSelectors"; import { selectTradeboxState } from "context/SyntheticsStateContext/selectors/tradeboxSelectors"; +import { + selectTradeboxLeverageSliderMarks, + selectTradeboxTradeFlags, +} from "context/SyntheticsStateContext/selectors/tradeboxSelectors"; import { useSelector } from "context/SyntheticsStateContext/utils"; import { TradeType } from "domain/synthetics/trade"; import { useLocalizedMap } from "lib/i18n"; -import { SwipeTabs } from "components/SwipeTabs/SwipeTabs"; +import { LeverageField } from "components/LeverageField/LeverageField"; import Tabs from "components/Tabs/Tabs"; +import { useIsCurtainOpen } from "components/TradeBox/Curtain"; +import { MarketPoolSelectorField } from "components/TradeBox/MarketPoolSelectorField"; +import { CollateralSelectorField } from "components/TradeBox/TradeBoxRows/CollateralSelectorField"; -import { mobileTradeTypeClassNames, tradeTypeClassNames, tradeTypeLabels } from "./tradeboxConstants"; +import ChevronDownIcon from "img/ic_chevron_down.svg?react"; -const OPTIONS = Object.values(TradeType); +import { tradeTypeClassNames, tradeTypeLabels } from "./tradeboxConstants"; export function TradeBoxHeaderTabs({ isInCurtain }: { isInCurtain?: boolean }) { const localizedTradeTypeLabels = useLocalizedMap(tradeTypeLabels); const history = useHistory(); - const { setTradeType: onSelectTradeType, tradeType } = useSelector(selectTradeboxState); + const { + setTradeType: onSelectTradeType, + tradeType, + leverageOption, + setLeverageOption, + marketInfo, + setCollateralAddress: onSelectCollateralAddress, + } = useSelector(selectTradeboxState); + const leverageSliderMarks = useSelector(selectTradeboxLeverageSliderMarks); + const { isIncrease, isPosition, isMarket } = useSelector(selectTradeboxTradeFlags); + const isLeverageSliderEnabled = useSelector(selectIsLeverageSliderEnabled); const onTradeTypeChange = useCallback( (type: TradeType) => { @@ -28,36 +47,117 @@ export function TradeBoxHeaderTabs({ isInCurtain }: { isInCurtain?: boolean }) { [history, onSelectTradeType, tradeType] ); - const tabsOptions = useMemo(() => { - return Object.values(TradeType).map((type) => ({ - value: type, - label: localizedTradeTypeLabels[type], - className: tradeTypeClassNames[type], - })); - }, [localizedTradeTypeLabels]); - - if (!isInCurtain) { - return ( - - ); - } + const longShortTabsOptions = useMemo( + () => + [TradeType.Long, TradeType.Short].map((type) => ({ + value: type, + label: localizedTradeTypeLabels[type], + className: tradeTypeClassNames[type], + })), + [localizedTradeTypeLabels] + ); + + const swapTabOptions = useMemo( + () => [ + { + value: TradeType.Swap, + label: localizedTradeTypeLabels[TradeType.Swap], + className: tradeTypeClassNames[TradeType.Swap], + }, + ], + [localizedTradeTypeLabels] + ); + + const leverageFieldVisible = isIncrease; + const fieldsColumnsClass = leverageFieldVisible ? "md:grid-cols-3" : "md:grid-cols-2"; + + const fields = ( +
+ {leverageFieldVisible ? ( + + ) : null} + +
+ +
+ +
+ +
+
+ ); + + const fieldsRow = ( +
+ {isPosition ? fields : null} +
+ ); + + const [isCurtainOpen, setIsCurtainOpen] = useIsCurtainOpen(); + + const handleToggleCurtain = (e: React.MouseEvent) => { + e.stopPropagation(); + setIsCurtainOpen(!isCurtainOpen); + }; + + const handleTabsClick = (e: React.MouseEvent) => { + e.stopPropagation(); + }; + + const isSwap = tradeType === TradeType.Swap; return ( - + <> + {isInCurtain && !isCurtainOpen ? null : fieldsRow} +
+
+ + +
+ {isInCurtain && ( + + )} +
+ ); } diff --git a/src/components/TradeBox/TradeBoxResponsiveContainer.tsx b/src/components/TradeBox/TradeBoxResponsiveContainer.tsx index 8abba70f1c..4e49a66c86 100644 --- a/src/components/TradeBox/TradeBoxResponsiveContainer.tsx +++ b/src/components/TradeBox/TradeBoxResponsiveContainer.tsx @@ -21,7 +21,7 @@ export function TradeBoxResponsiveContainer() { } return ( - } dataQa="tradebox"> + } dataQa="tradebox" hideChevron headerHeight={48}> diff --git a/src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx b/src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx index 688a55c4ad..dce8e778d5 100644 --- a/src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx +++ b/src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx @@ -197,6 +197,7 @@ export function TradeBoxAdvancedGroups({ hasError={hasError} contentClassName="flex flex-col gap-14" scrollIntoViewOnMobile + wrapped > {isTrigger ? ( diff --git a/src/components/TradeBox/TradeBoxRows/AllowedSlippageRow.tsx b/src/components/TradeBox/TradeBoxRows/AllowedSlippageRow.tsx index fc17a1e03f..34d952ff7e 100644 --- a/src/components/TradeBox/TradeBoxRows/AllowedSlippageRow.tsx +++ b/src/components/TradeBox/TradeBoxRows/AllowedSlippageRow.tsx @@ -36,7 +36,7 @@ export function AllowedSlippageRow({ slippageInputId }: { slippageInputId: strin diff --git a/src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx b/src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx similarity index 80% rename from src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx rename to src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx index 7a752d61bf..d29504e006 100644 --- a/src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +++ b/src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx @@ -1,5 +1,5 @@ import { Trans } from "@lingui/macro"; -import React, { useMemo } from "react"; +import React, { useMemo, useRef } from "react"; import { selectTradeboxAvailableMarketsOptions, @@ -15,8 +15,8 @@ import { selectTradeboxAvailableAndDisabledTokensForCollateral } from "context/S import { useSelector } from "context/SyntheticsStateContext/utils"; import { AlertInfoCard } from "components/AlertInfo/AlertInfoCard"; +import { BlockField } from "components/BlockField/BlockField"; import { ColorfulButtonLink } from "components/ColorfulBanner/ColorfulBanner"; -import { SyntheticsInfoRow } from "components/SyntheticsInfoRow"; import TooltipWithPortal from "components/Tooltip/TooltipWithPortal"; import { CollateralSelector } from "../../CollateralSelector/CollateralSelector"; @@ -28,38 +28,50 @@ export type Props = { isMarket: boolean; }; -export function CollateralSelectorRow(p: Props) { +export function CollateralSelectorField(p: Props) { const { onSelectCollateralAddress } = p; const selectedTokenName = useSelector(selectTradeboxSelectedCollateralTokenSymbol); + const popoverReferenceRef = useRef(null); const { availableTokens, disabledTokens } = useSelector(selectTradeboxAvailableAndDisabledTokensForCollateral); - const warnings = useCollateralWarnings(); const collateralInTooltipContent = useCollateralInTooltipContent(); return ( - <> - - Collateral In - - } - value={ - - } - /> - {warnings} - + + + Collateral + + + } + labelClassName="overflow-hidden shrink-1 grow-0 flex" + contentClassName="shrink-0 min-w-[unset]" + className="group/selector-field overflow-hidden" + content={ + + } + /> ); } -function useCollateralWarnings() { +export function useCollateralWarnings() { const selectedMarketAddress = useSelector(selectTradeboxMarketAddress); const selectedCollateralAddress = useSelector(selectTradeboxCollateralTokenAddress); const { isMarket } = useSelector(selectTradeboxTradeFlags); diff --git a/src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx b/src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx deleted file mode 100644 index e4f4f99321..0000000000 --- a/src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +++ /dev/null @@ -1,225 +0,0 @@ -import { t, Trans } from "@lingui/macro"; -import { useCallback, useMemo, useRef } from "react"; - -import { selectSelectedMarketVisualMultiplier } from "context/SyntheticsStateContext/selectors/statsSelectors"; -import { - selectTradeboxAdvancedOptions, - selectTradeboxSetAdvancedOptions, - selectTradeboxTradeFlags, -} from "context/SyntheticsStateContext/selectors/tradeboxSelectors"; -import { useSelector } from "context/SyntheticsStateContext/utils"; -import { SidecarOrderEntryGroup } from "domain/synthetics/sidecarOrders/types"; -import { useSidecarEntries } from "domain/synthetics/sidecarOrders/useSidecarEntries"; -import { useSidecarOrders } from "domain/synthetics/sidecarOrders/useSidecarOrders"; -import { PERCENTAGE_DECIMALS } from "domain/synthetics/sidecarOrders/utils"; -import { formatAmount, formatPercentage, formatUsd, formatUsdPrice } from "lib/numbers"; - -import { ExpandableRow } from "components/ExpandableRow"; -import { SyntheticsInfoRow } from "components/SyntheticsInfoRow"; -import TooltipWithPortal from "components/Tooltip/TooltipWithPortal"; - -import PlusIcon from "img/ic_plus.svg?react"; - -import { EntryButton } from "../components/EntryButton"; -import { SideOrderEntries } from "../components/SideOrderEntries"; - -function SideOrders({ type }: { type: "stopLoss" | "takeProfit" | "limit" }) { - const { stopLoss, takeProfit, limit } = useSidecarOrders(); - const visualMultiplier = useSelector(selectSelectedMarketVisualMultiplier); - const containerRef = useRef(null); - - const isStopLoss = type === "stopLoss"; - const isLimitGroup = type === "limit"; - - const entriesInfo: SidecarOrderEntryGroup = { - stopLoss: stopLoss, - takeProfit: takeProfit, - limit: limit, - }[type]; - - const handleAddEntry = useCallback(() => { - entriesInfo.addEntry(); - - requestAnimationFrame(() => { - const inputs = containerRef.current?.querySelectorAll(".SideOrderInput"); - (inputs && (inputs[inputs.length - 1] as HTMLInputElement))?.focus(); - }); - }, [entriesInfo]); - - if (!entriesInfo || entriesInfo.entries.every((e) => e.txnType === "cancel")) return; - - const label = { - stopLoss: t`Stop Loss`, - takeProfit: t`Take Profit`, - limit: t`Limit`, - }[type]; - - const labelPnl = isStopLoss ? t`Stop Loss PnL` : t`Take Profit PnL`; - - return ( - <> - - {label} - {entriesInfo.canAddEntry && ( - - - - - - )} -
- } - valueClassName="-my-5" - value={ -
- -
- } - /> - {(!isLimitGroup && entriesInfo.totalPnL !== undefined && entriesInfo.totalPnLPercentage !== undefined && ( - - {entriesInfo.totalPnL === 0n ? ( - "-" - ) : ( - - entriesInfo?.entries?.map((entry, index) => { - if (!entry || !entry.decreaseAmounts || entry.txnType === "cancel") return; - - const price = formatUsdPrice(entry.price?.value ?? undefined, { - visualMultiplier, - }); - const percentage = - entry.percentage?.value && formatAmount(entry.percentage.value, PERCENTAGE_DECIMALS, 0); - - return ( -
- {(price && percentage && ( - - At {price}, {isStopLoss ? "SL" : "TP"} {percentage}%: - - )) || - null} - - - {formatUsd(entry.decreaseAmounts?.realizedPnl)} ( - {formatPercentage(entry.decreaseAmounts?.realizedPnlPercentage, { - signed: true, - })} - ) - -
- ); - }) - } - /> - )} -
- )) || - null} - - ); -} - -export function LimitAndTPSLRows({ hasExistingLimitOrder }: { hasExistingLimitOrder: boolean }) { - return ( -
- - {hasExistingLimitOrder &&
} - -
- -
- ); -} - -export function LimitAndTPSLGroup() { - const options = useSelector(selectTradeboxAdvancedOptions); - const setOptions = useSelector(selectTradeboxSetAdvancedOptions); - const { isTrigger, isSwap } = useSelector(selectTradeboxTradeFlags); - - const showTPSL = !isTrigger && !isSwap; - - const entries = useSidecarEntries(); - const orders = useSidecarOrders(); - - const hasError = useMemo(() => { - const hasAnyEntryError = entries.some((e) => { - if (e.txnType === "cancel") return false; - - return e.sizeUsd?.error || e.percentage?.error || e.price?.error; - }); - return Boolean(orders.stopLoss.error?.percentage || orders.takeProfit.error?.percentage || hasAnyEntryError); - }, [entries, orders]); - - const isTpSlVisible = hasError ? true : options.limitOrTPSL; - - const hasExistingLimitOrder = useMemo(() => { - return orders.limit.entries.length > 0; - }, [orders.limit.entries]); - - const toggleLimitOrTPSL = useCallback( - (value: boolean) => { - setOptions((ops) => ({ - ...ops, - limitOrTPSL: value, - })); - }, - [setOptions] - ); - - if (!showTPSL) { - return null; - } - - return ( - Limit / Take Profit / Stop Loss : Take Profit / Stop Loss - } - hasError={hasError} - disableCollapseOnError - autoExpandOnError - errorMessage={There are issues in the TP/SL orders.} - onToggle={toggleLimitOrTPSL} - withToggleSwitch - > - - - ); -} diff --git a/src/components/TradeBox/TradeBoxRows/TPSLRows.tsx b/src/components/TradeBox/TradeBoxRows/TPSLRows.tsx new file mode 100644 index 0000000000..98643e4b09 --- /dev/null +++ b/src/components/TradeBox/TradeBoxRows/TPSLRows.tsx @@ -0,0 +1,108 @@ +import { Trans } from "@lingui/macro"; +import { useCallback, useMemo } from "react"; + +import { + selectTradeboxAdvancedOptions, + selectTradeboxIsTPSLEnabled, + selectTradeboxSetAdvancedOptions, + selectTradeboxTradeFlags, +} from "context/SyntheticsStateContext/selectors/tradeboxSelectors"; +import { useSelector } from "context/SyntheticsStateContext/utils"; +import { useSidecarEntries } from "domain/synthetics/sidecarOrders/useSidecarEntries"; +import { useSidecarOrders } from "domain/synthetics/sidecarOrders/useSidecarOrders"; + +import { ExpandableRow } from "components/ExpandableRow"; +import TooltipWithPortal from "components/Tooltip/TooltipWithPortal"; + +import { SideOrderEntry } from "../components/SideOrderEntry"; + +function SideOrders({ type }: { type: "stopLoss" | "takeProfit" }) { + const { stopLoss, takeProfit } = useSidecarOrders(); + + if (type === "takeProfit") { + const entry = takeProfit.entries.find((e) => e.txnType !== "cancel"); + if (!entry) return null; + return ; + } + + const entry = stopLoss.entries.find((e) => e.txnType !== "cancel"); + if (!entry) return null; + return ; +} + +export function TPSLRows() { + return ( +
+ + +
+ ); +} + +export function TPSLGroup() { + const options = useSelector(selectTradeboxAdvancedOptions); + const setOptions = useSelector(selectTradeboxSetAdvancedOptions); + const { isTrigger, isSwap } = useSelector(selectTradeboxTradeFlags); + const isTpSlEnabled = useSelector(selectTradeboxIsTPSLEnabled); + + const showTPSL = !isTrigger && !isSwap; + + const entries = useSidecarEntries(); + const orders = useSidecarOrders(); + + const hasError = useMemo(() => { + if (!isTpSlEnabled) { + return false; + } + + const hasAnyEntryError = entries.some((e) => { + if (e.txnType === "cancel") return false; + + return e.sizeUsd?.error || e.percentage?.error || e.price?.error; + }); + return Boolean(orders.stopLoss.error?.percentage || orders.takeProfit.error?.percentage || hasAnyEntryError); + }, [entries, isTpSlEnabled, orders]); + + const isTpSlVisible = hasError ? true : options.limitOrTPSL; + + const toggleTPSL = useCallback( + (value: boolean) => { + setOptions((ops) => ({ + ...ops, + limitOrTPSL: value, + })); + }, + [setOptions] + ); + + if (!showTPSL) { + return null; + } + + return ( + Take Profit / Stop Loss} + variant="iconStroke" + position="top" + content={ + + Create basic TP/SL orders that fully close your position. For advanced TP/SL setup, use the positions list + after opening a position. + + } + /> + } + hasError={hasError} + disableCollapseOnError + autoExpandOnError + errorMessage={There are issues in the TP/SL orders.} + onToggle={toggleTPSL} + withToggleSwitch + > + + + ); +} diff --git a/src/components/TradeBox/components/SideOrderEntries.tsx b/src/components/TradeBox/components/SideOrderEntries.tsx deleted file mode 100644 index f1c2edf24f..0000000000 --- a/src/components/TradeBox/components/SideOrderEntries.tsx +++ /dev/null @@ -1,203 +0,0 @@ -import { t } from "@lingui/macro"; -import { useCallback, useMemo, useRef } from "react"; - -import { selectTradeboxMarketInfo } from "context/SyntheticsStateContext/selectors/tradeboxSelectors"; -import { useSelector } from "context/SyntheticsStateContext/utils"; -import { isIncreaseOrderType } from "domain/synthetics/orders"; -import { SidecarOrderEntry, SidecarOrderEntryGroup } from "domain/synthetics/sidecarOrders/useSidecarOrders"; -import { TokenData } from "domain/synthetics/tokens"; -import { formatUsd, formatUsdPrice } from "lib/numbers"; -import { getTokenVisualMultiplier } from "sdk/configs/tokens"; - -import { NUMBER_WITH_TWO_DECIMALS } from "components/PercentageInput/PercentageInput"; -import SuggestionInput from "components/SuggestionInput/SuggestionInput"; -import TooltipWithPortal from "components/Tooltip/TooltipWithPortal"; - -import PlusIcon from "img/ic_plus.svg?react"; - -import { EntryButton } from "./EntryButton"; - -const SUGGESTION_PERCENTAGE_LIST = [10, 25, 50, 75, 100]; - -interface SidecarEntryProps { - entry: SidecarOrderEntry; - commonError?: { - price?: string; - percentage?: string; - } | null; - indexToken?: TokenData; - displayMode: "sizeUsd" | "percentage"; - updateEntry: (id: string, field: "sizeUsd" | "percentage" | "price", value: string) => void; - deleteEntry: (id: string) => void; - entriesCount: number; -} - -function SideOrderEntry({ - entry, - commonError, - indexToken, - displayMode, - entriesCount, - updateEntry, - deleteEntry, -}: SidecarEntryProps) { - const percentageError = commonError?.percentage || entry.percentage?.error; - const priceError = commonError?.price || entry.price?.error; - const sizeError = displayMode === "percentage" ? percentageError : entry.sizeUsd?.error; - - const isIncrease = entry.order && isIncreaseOrderType(entry.order.orderType); - const isLong = entry.order?.isLong; - - const priceTooltipMsg = - indexToken && - entry.price?.value && - entry.sizeUsd?.value && - `${isIncrease ? "Increase" : "Decrease"} ${getTokenVisualMultiplier(indexToken)}${ - indexToken.baseSymbol || indexToken.symbol - } ${isLong ? "Long" : "Short"} by ${formatUsd(entry.sizeUsd.value)} at ${formatUsdPrice( - entry.price.value ?? undefined, - { - visualMultiplier: indexToken.visualMultiplier, - } - )}.`; - - const sizeTooltipMsg = - sizeError || priceTooltipMsg ? ( - <> - {sizeError} - {sizeError && priceTooltipMsg && ( - <> -
-
- - )} - {priceTooltipMsg} - - ) : null; - - const onPriceValueChange = useCallback( - (value) => { - updateEntry(entry.id, "price", value); - }, - [updateEntry, entry.id] - ); - - const onSizeUsdValueChange = useCallback( - (value) => { - updateEntry(entry.id, "sizeUsd", value); - }, - [updateEntry, entry.id] - ); - - const onPercentageSetValue = useCallback( - (value) => { - if (NUMBER_WITH_TWO_DECIMALS.test(value) || value.length === 0) { - updateEntry(entry.id, "percentage", value); - } - }, - [updateEntry, entry.id] - ); - - const onDeleteEntry = useCallback(() => deleteEntry(entry.id), [deleteEntry, entry.id]); - - return ( -
- - - - {displayMode === "percentage" && ( -
- - - -
- )} - {displayMode === "sizeUsd" && ( - - - - )} - - - - -
- ); -} - -type SidecarEntriesProps = { - entriesInfo: SidecarOrderEntryGroup; - displayMode: "percentage" | "sizeUsd"; -}; - -export function SideOrderEntries({ entriesInfo, displayMode }: SidecarEntriesProps) { - const marketInfo = useSelector(selectTradeboxMarketInfo); - const { updateEntry, deleteEntry } = entriesInfo; - const containerRef = useRef(null); - - const displayableEntries = useMemo( - () => entriesInfo.entries.filter((entry) => entry.txnType !== "cancel"), - [entriesInfo] - ); - - return ( -
- {displayableEntries?.map((entry) => ( - - ))} -
- ); -} diff --git a/src/components/TradeBox/components/SideOrderEntry.tsx b/src/components/TradeBox/components/SideOrderEntry.tsx new file mode 100644 index 0000000000..653e586fa8 --- /dev/null +++ b/src/components/TradeBox/components/SideOrderEntry.tsx @@ -0,0 +1,88 @@ +import { useCallback, useMemo } from "react"; + +import { + selectTradeboxMarketInfo, + selectTradeboxTradeFlags, +} from "context/SyntheticsStateContext/selectors/tradeboxSelectors"; +import { selectTradeboxMockPosition } from "context/SyntheticsStateContext/selectors/tradeboxSelectors/selectTradeboxSidecarOrders"; +import { useSelector } from "context/SyntheticsStateContext/utils"; +import { SidecarSlTpOrderEntry, SidecarOrderEntryGroupBase } from "domain/synthetics/sidecarOrders/types"; +import { buildTpSlInputPositionData } from "domain/tpsl/sidecar"; +import { expandDecimals } from "lib/numbers"; +import { bigMath } from "sdk/utils/bigmath"; + +import { TPSLInputRow } from "components/TPSLModal/TPSLInputRow"; + +export type TPSLDisplayMode = "percentage" | "usd"; + +type Props = { + type: "takeProfit" | "stopLoss"; + entry: SidecarSlTpOrderEntry; + entriesInfo: SidecarOrderEntryGroupBase; +}; + +export function SideOrderEntry({ type, entry, entriesInfo }: Props) { + const mockPosition = useSelector(selectTradeboxMockPosition); + const marketInfo = useSelector(selectTradeboxMarketInfo); + const { isLong } = useSelector(selectTradeboxTradeFlags); + + const priceError = entry.price?.error ?? undefined; + + const collateralUsd = useMemo(() => { + const collateralAmount = mockPosition?.collateralAmount; + const collateralToken = mockPosition?.collateralToken; + if (collateralAmount === undefined) return 0n; + if (collateralToken?.decimals === undefined || collateralToken?.prices?.minPrice === undefined) return 0n; + + return bigMath.mulDiv( + collateralAmount, + collateralToken.prices.minPrice, + expandDecimals(1, collateralToken.decimals) + ); + }, [mockPosition?.collateralAmount, mockPosition?.collateralToken]); + + const positionData = useMemo( + () => + buildTpSlInputPositionData({ + position: mockPosition ?? { + sizeInUsd: 0n, + sizeInTokens: 0n, + collateralUsd: 0n, + entryPrice: 0n, + liquidationPrice: undefined, + isLong, + }, + collateralUsd, + indexTokenDecimals: marketInfo?.indexToken?.decimals ?? 18, + visualMultiplier: marketInfo?.indexToken?.visualMultiplier ?? 1, + })!, + [mockPosition, collateralUsd, isLong, marketInfo] + ); + + const handlePriceChange = useCallback( + (value: string) => { + entriesInfo.updateEntry(entry.id, "price", value); + }, + [entriesInfo, entry.id] + ); + + const estimatedPnl = useMemo(() => { + if (!entry.decreaseAmounts) return undefined; + return { + pnlUsd: entry.decreaseAmounts.realizedPnl, + pnlPercentage: entry.decreaseAmounts.realizedPnlPercentage, + }; + }, [entry.decreaseAmounts]); + + return ( + + ); +} diff --git a/src/components/TradeBox/hooks/useSidecarOrderPayloads.ts b/src/components/TradeBox/hooks/useSidecarOrderPayloads.ts index ea6399e758..3eb6ce23d1 100644 --- a/src/components/TradeBox/hooks/useSidecarOrderPayloads.ts +++ b/src/components/TradeBox/hooks/useSidecarOrderPayloads.ts @@ -1,6 +1,5 @@ import { useMemo } from "react"; -import { UI_FEE_RECEIVER_ACCOUNT } from "config/ui"; import { selectAccount, selectUserReferralInfo } from "context/SyntheticsStateContext/selectors/globalSelectors"; import { selectTradeboxCollateralTokenAddress, @@ -10,8 +9,9 @@ import { } from "context/SyntheticsStateContext/selectors/tradeboxSelectors"; import { useSelector } from "context/SyntheticsStateContext/utils"; import { useMaxAutoCancelOrdersState } from "domain/synthetics/trade/useMaxAutoCancelOrdersState"; +import { buildTpSlCreatePayloads } from "domain/tpsl/sidecar"; import { useChainId } from "lib/chains"; -import { buildDecreaseOrderPayload, buildUpdateOrderPayload, CancelOrderTxnParams } from "sdk/utils/orderTransactions"; +import { buildUpdateOrderPayload, CancelOrderTxnParams } from "sdk/utils/orderTransactions"; import { useRequiredActions } from "./useRequiredActions"; import { useTPSLSummaryExecutionFee } from "./useTPSLSummaryExecutionFee"; @@ -34,35 +34,19 @@ export function useSidecarOrderPayloads() { return undefined; } - const createPayloads = createSltpEntries.map((entry, i) => { - const amounts = entry.decreaseAmounts; - - return buildDecreaseOrderPayload({ - chainId, - receiver: account, - collateralDeltaAmount: amounts.collateralDeltaAmount ?? 0n, - collateralTokenAddress: collateralAddress, - sizeDeltaUsd: amounts.sizeDeltaUsd, - sizeDeltaInTokens: amounts.sizeDeltaInTokens, - referralCode: userReferralInfo?.referralCodeForTxn, - uiFeeReceiver: UI_FEE_RECEIVER_ACCOUNT, - allowedSlippage: 0, - orderType: amounts.triggerOrderType!, - autoCancel: i < autoCancelOrdersLimit, - swapPath: [], - externalSwapQuote: undefined, - marketAddress: marketInfo.marketTokenAddress, - indexTokenAddress: marketInfo.indexTokenAddress, - isLong, - acceptablePrice: amounts.acceptablePrice, - triggerPrice: amounts.triggerPrice, - receiveTokenAddress: collateralAddress, - minOutputUsd: 0n, - decreasePositionSwapType: amounts.decreaseSwapType, + const createPayloads = buildTpSlCreatePayloads({ + autoCancelOrdersLimit, + chainId, + account, + marketAddress: marketInfo.marketTokenAddress, + indexTokenAddress: marketInfo.indexTokenAddress, + collateralTokenAddress: collateralAddress, + isLong, + entries: createSltpEntries.map((entry) => ({ + amounts: entry.decreaseAmounts, executionFeeAmount: getExecutionFeeAmountForEntry(entry) ?? 0n, - executionGasLimit: 0n, // Don't need for tp/sl entries - validFromTime: 0n, - }); + })), + userReferralCode: userReferralInfo?.referralCodeForTxn, }); const updatePayloads = updateSltpEntries.map((entry) => { diff --git a/src/components/TradeBox/hooks/useTradeButtonState.tsx b/src/components/TradeBox/hooks/useTradeButtonState.tsx index 552f3ac7d0..8bf094b6cb 100644 --- a/src/components/TradeBox/hooks/useTradeButtonState.tsx +++ b/src/components/TradeBox/hooks/useTradeButtonState.tsx @@ -30,12 +30,11 @@ import { selectSavedAcceptablePriceImpactBuffer } from "context/SyntheticsStateC import { selectTokenPermits } from "context/SyntheticsStateContext/selectors/tokenPermitsSelectors"; import { selectExternalSwapQuote, - selectTradeboxDecreasePositionAmounts, selectTradeboxFindSwapPath, selectTradeboxFromToken, selectTradeboxFromTokenAmount, - selectTradeboxIncreasePositionAmounts, selectTradeboxIsFromTokenGmxAccount, + selectTradeboxIsTPSLEnabled, selectTradeboxIsStakeOrUnstake, selectTradeboxIsWrapOrUnwrap, selectTradeboxMaxLeverage, @@ -46,6 +45,7 @@ import { selectTradeboxToToken, selectTradeboxToTokenAmount, selectTradeboxTradeFlags, + selectTradeboxTradeMode, selectTradeboxTriggerPrice, } from "context/SyntheticsStateContext/selectors/tradeboxSelectors"; import { selectTradeboxTradeTypeError } from "context/SyntheticsStateContext/selectors/tradeboxSelectors/selectTradeboxTradeErrors"; @@ -53,7 +53,7 @@ import { selectExternalSwapQuoteParams } from "context/SyntheticsStateContext/se import { useSelector } from "context/SyntheticsStateContext/utils"; import { useGmxAccountShowDepositButton } from "domain/multichain/useGmxAccountShowDepositButton"; import { ExpressTxnParams } from "domain/synthetics/express"; -import { getNameByOrderType, substractMaxLeverageSlippage } from "domain/synthetics/positions/utils"; +import { substractMaxLeverageSlippage } from "domain/synthetics/positions/utils"; import { useSidecarEntries } from "domain/synthetics/sidecarOrders/useSidecarEntries"; import { useSidecarOrders } from "domain/synthetics/sidecarOrders/useSidecarOrders"; import { getApprovalRequirements } from "domain/synthetics/tokens/utils"; @@ -69,7 +69,7 @@ import { mustNeverExist } from "lib/types"; import { useHasOutdatedUi } from "lib/useHasOutdatedUi"; import { sendUserAnalyticsConnectWalletClickEvent } from "lib/userAnalytics"; import { useEthersSigner } from "lib/wallets/useEthersSigner"; -import { getToken, getTokenBySymbol, getTokenVisualMultiplier } from "sdk/configs/tokens"; +import { getToken, getTokenBySymbol } from "sdk/configs/tokens"; import { ExecutionFee } from "sdk/types/fees"; import { TokenData } from "sdk/types/tokens"; import { TradeMode, TradeType } from "sdk/types/trade"; @@ -81,7 +81,7 @@ import ExternalLink from "components/ExternalLink/ExternalLink"; import SpinnerIcon from "img/ic_spinner.svg?react"; -import { tradeTypeLabels } from "../tradeboxConstants"; +import { tradeModeLabels, tradeTypeLabels } from "../tradeboxConstants"; import { useTradeboxTransactions } from "./useTradeboxTransactions"; interface TradeboxButtonStateOptions { @@ -109,11 +109,14 @@ export function useTradeboxButtonState({ const signer = useEthersSigner(); const tradeFlags = useSelector(selectTradeboxTradeFlags); - const { isSwap, isIncrease, isLimit, isMarket, isTwap } = tradeFlags; + const { isSwap, isIncrease } = tradeFlags; const { stopLoss, takeProfit } = useSidecarOrders(); const sidecarEntries = useSidecarEntries(); + const isTpSlEnabled = useSelector(selectTradeboxIsTPSLEnabled); const hasOutdatedUi = useHasOutdatedUi(); const localizedTradeTypeLabels = useLocalizedMap(tradeTypeLabels); + const localizedTradeModeLabels = useLocalizedMap(tradeModeLabels); + const tradeMode = useSelector(selectTradeboxTradeMode); const { stage, collateralToken, tradeType, setStage } = useSelector(selectTradeboxState); const { isLeverageSliderEnabled } = useSettings(); const { shouldShowDepositButton } = useGmxAccountShowDepositButton(); @@ -124,8 +127,6 @@ export function useTradeboxButtonState({ const fromToken = useSelector(selectTradeboxFromToken); const toToken = useSelector(selectTradeboxToToken); const gasPaymentToken = useSelector(selectGasPaymentToken); - const increaseAmounts = useSelector(selectTradeboxIncreasePositionAmounts); - const decreaseAmounts = useSelector(selectTradeboxDecreasePositionAmounts); const tokensData = useSelector(selectTokensData); const isWrapOrUnwrap = useSelector(selectTradeboxIsWrapOrUnwrap); const isStakeOrUnstake = useSelector(selectTradeboxIsStakeOrUnstake); @@ -445,7 +446,7 @@ export function useTradeboxButtonState({ }; } - if (stopLoss.error?.percentage || takeProfit.error?.percentage) { + if (isTpSlEnabled && (stopLoss.error?.percentage || takeProfit.error?.percentage)) { return { ...commonState, text: t`TP/SL orders exceed the position`, @@ -499,25 +500,14 @@ export function useTradeboxButtonState({ { if (buttonErrorText) { submitButtonText = buttonErrorText; - } + } else { + const modeLabel = localizedTradeModeLabels[tradeMode]; - if (isMarket) { if (isSwap) { - submitButtonText = t`Swap ${fromToken?.symbol}`; + submitButtonText = `${modeLabel}: ${t`Swap`} ${fromToken?.symbol}`; } else { - if (!toToken?.symbol) { - submitButtonText = `${localizedTradeTypeLabels[tradeType!]} ...`; - } - const prefix = toToken ? getTokenVisualMultiplier(toToken) : ""; - - submitButtonText = `${localizedTradeTypeLabels[tradeType!]} ${prefix}${toToken?.symbol}`; + submitButtonText = `${modeLabel}: ${localizedTradeTypeLabels[tradeType!]} ${isIncrease ? t`Increase` : t`Decrease`}`; } - } else if (isLimit) { - submitButtonText = t`Create ${getNameByOrderType(increaseAmounts?.limitOrderType, false)} order`; - } else if (isTwap) { - submitButtonText = t`Create TWAP ${isSwap ? "Swap" : "Increase"} order`; - } else { - submitButtonText = t`Create ${getNameByOrderType(decreaseAmounts?.triggerOrderType, false)} order`; } } @@ -559,17 +549,14 @@ export function useTradeboxButtonState({ stage, isIncrease, sidecarEntries, + isTpSlEnabled, chainId, - isMarket, - isLimit, - isTwap, isSwap, fromToken?.symbol, - toToken, localizedTradeTypeLabels, + localizedTradeModeLabels, + tradeMode, tradeType, - increaseAmounts?.limitOrderType, - decreaseAmounts?.triggerOrderType, isFromTokenGmxAccount, ]); } diff --git a/src/components/TradeBox/hooks/useTradeboxChanges.ts b/src/components/TradeBox/hooks/useTradeboxChanges.ts index 819c55b063..ce1c9b49a1 100644 --- a/src/components/TradeBox/hooks/useTradeboxChanges.ts +++ b/src/components/TradeBox/hooks/useTradeboxChanges.ts @@ -8,6 +8,7 @@ import { selectTradeboxTradeFlags, selectTradeboxTriggerPrice, selectTradeboxFromTokenInputValue, + selectTradeboxToTokenInputValue, } from "context/SyntheticsStateContext/selectors/tradeboxSelectors"; import { useSelector } from "context/SyntheticsStateContext/utils"; @@ -17,7 +18,7 @@ export function useTradeboxChanges() { const fromTokenAddress = useSelector(selectTradeboxFromTokenAddress); const toTokenAddress = useSelector(selectTradeboxToTokenAddress); const fromTokenInputValue = useSelector(selectTradeboxFromTokenInputValue); - const toTokenInputValue = useSelector(selectTradeboxFromTokenInputValue); + const toTokenInputValue = useSelector(selectTradeboxToTokenInputValue); const previousMarketIndexTokenAddress = usePrevious(marketInfo?.indexTokenAddress); const tradeFlags = useSelector(selectTradeboxTradeFlags); diff --git a/src/components/TradeBox/hooks/useTradeboxTPSLReset.ts b/src/components/TradeBox/hooks/useTradeboxTPSLReset.ts index d61e0fb8be..6bdfc501ca 100644 --- a/src/components/TradeBox/hooks/useTradeboxTPSLReset.ts +++ b/src/components/TradeBox/hooks/useTradeboxTPSLReset.ts @@ -31,14 +31,18 @@ export function useTradeboxTPSLReset(setIsDismissed: (isDismissed: boolean) => v fromTokenAddress !== previouseFromTokenAddress || toTokenAddress !== previousToTokenAddress || isLong !== previousIsLong || - marketAddress !== previousMarketAddress || - collateralToken !== previousCollateralToken || isIncrease !== previousIsIncrease; + const shouldResetPriceImpactWarning = + shouldResetLimitOrTPSL || marketAddress !== previousMarketAddress || collateralToken !== previousCollateralToken; + useEffect(() => { - if (shouldResetLimitOrTPSL) { + if (shouldResetPriceImpactWarning) { setIsDismissed(false); + } + + if (shouldResetLimitOrTPSL) { reset(); } - }, [reset, shouldResetLimitOrTPSL, setIsDismissed]); + }, [reset, setIsDismissed, shouldResetLimitOrTPSL, shouldResetPriceImpactWarning]); } diff --git a/src/components/TradeBox/tradeboxConstants.tsx b/src/components/TradeBox/tradeboxConstants.tsx index 327a061727..48e7ddfa2c 100644 --- a/src/components/TradeBox/tradeboxConstants.tsx +++ b/src/components/TradeBox/tradeboxConstants.tsx @@ -33,18 +33,15 @@ export const tradeTypeLabels = { */ export const tradeTypeClassNames = { [TradeType.Long]: { - active: "!bg-green-500/20 border-b-green-500 pb-9", + active: "!bg-green-900 border-b-green-500 pb-9 !text-green-100", + regular: "border-b-transparent", }, [TradeType.Short]: { - active: "!bg-red-500/20 border-b-red-500 pb-9", + active: "!bg-red-900 border-b-red-500 pb-9 !text-red-100", + regular: "border-b-transparent", }, [TradeType.Swap]: { - active: "!bg-blue-300/20 border-b-blue-300 pb-9", + active: "!bg-blue-300/10 border-b-blue-300 pb-9 !text-blue-300", + regular: "border-b-transparent", }, }; - -export const mobileTradeTypeClassNames = { - [TradeType.Long]: "!bg-green-500/20 border-b-2 border-b-green-500", - [TradeType.Short]: "!bg-red-500/20 border-b-2 border-b-red-500", - [TradeType.Swap]: "!bg-blue-300/20 border-b-2 border-b-blue-300", -}; diff --git a/src/components/TradeInfoIcon/TradeInfoIcon.tsx b/src/components/TradeInfoIcon/TradeInfoIcon.tsx index 791129caea..a10652e78d 100644 --- a/src/components/TradeInfoIcon/TradeInfoIcon.tsx +++ b/src/components/TradeInfoIcon/TradeInfoIcon.tsx @@ -58,16 +58,6 @@ export default function TradeBoxLongShortInfoIcon({ tradePlace, tradeType, isMob . ) : null} -
  • - - {typeString} Long TP/SL: {getTradeTypeLabel(TradeMode.Trigger, tradePlace)} a long position when the trigger - price is reached. - {" "} - - Read more - - . -
  • {isTradeBox ? (
  • @@ -112,16 +102,6 @@ export default function TradeBoxLongShortInfoIcon({ tradePlace, tradeType, isMob .
  • ) : null} -
  • - - {typeString} Short TP/SL: {getTradeTypeLabel(TradeMode.Trigger, tradePlace)} a short position when the - trigger price is reached. - {" "} - - Read more - - . -
  • {isTradeBox ? (
  • diff --git a/src/components/LeverageSlider/LeverageSlider.scss b/src/components/TradeboxMarginFields/MarginPercentageSlider.scss similarity index 74% rename from src/components/LeverageSlider/LeverageSlider.scss rename to src/components/TradeboxMarginFields/MarginPercentageSlider.scss index 25a6c8341f..f2bc1587d1 100644 --- a/src/components/LeverageSlider/LeverageSlider.scss +++ b/src/components/TradeboxMarginFields/MarginPercentageSlider.scss @@ -1,19 +1,6 @@ $blue: var(--color-blue-300); -$gray: var(--color-slate-600); - -.LeverageSlider { - margin-top: 1.5rem; - margin-bottom: 3.4rem; - - &.slim { - margin-top: 0px; - margin-bottom: 0px; - } - - padding: 0 8px 0 12px; - - user-select: none; +.MarginPercentageSlider { .rc-slider-tooltip-inner { box-shadow: none; padding: 0.465rem 0.8rem; @@ -21,7 +8,6 @@ $gray: var(--color-slate-600); .rc-slider-tooltip { z-index: 5; - background: rgb(49, 54, 85); box-shadow: 0px 4px 14px rgba(0, 0, 0, 0.25); } @@ -31,15 +17,15 @@ $gray: var(--color-slate-600); } .rc-slider-rail { - border-radius: 0; + border-radius: 2px; height: 4px; background: var(--color-slate-800); } .rc-slider-track { - border-radius: 0; + border-radius: 2px; height: 4px; - background-color: var(--color-blue-300); + background-color: $blue; } .rc-slider-dot { @@ -51,9 +37,11 @@ $gray: var(--color-slate-600); box-sizing: content-box; top: -1px; } + .rc-slider-dot-active { - border-color: var(--color-blue-300); + border-color: $blue; } + .rc-slider-handle { background-color: var(--color-slate-800); border: solid 2px $blue; @@ -67,6 +55,10 @@ $gray: var(--color-slate-600); } } + .rc-slider-mark { + top: 12px !important; + } + .rc-slider-mark-text, .rc-slider-mark-text-active { color: var(--color-slate-100); @@ -77,6 +69,20 @@ $gray: var(--color-slate-600); text-align: left; } + .rc-slider-mark-text:nth-child(2n) { + display: none; + } + + .rc-slider-mark-text:first-child { + left: 3px !important; + } + + .rc-slider-mark-text:last-child { + right: -3px !important; + left: unset !important; + transform: translateX(0) !important; + } + .rc-slider-mark-text:hover, .rc-slider-mark-text-active:hover { opacity: 1; diff --git a/src/components/TradeboxMarginFields/MarginPercentageSlider.tsx b/src/components/TradeboxMarginFields/MarginPercentageSlider.tsx new file mode 100644 index 0000000000..a1afcdd04f --- /dev/null +++ b/src/components/TradeboxMarginFields/MarginPercentageSlider.tsx @@ -0,0 +1,129 @@ +import { Trans } from "@lingui/macro"; +import cx from "classnames"; +import Slider from "rc-slider"; +import { ChangeEvent, useCallback, useEffect, useMemo, useState } from "react"; + +import "rc-slider/assets/index.css"; +import "./MarginPercentageSlider.scss"; + +const PERCENTAGE_MARKS = [0, 25, 50, 75, 100]; +const clampPercentage = (value: number) => { + if (!Number.isFinite(value)) { + return 0; + } + + return Math.min(100, Math.max(0, Math.round(value))); +}; + +type Props = { + value: number; + onChange: (value: number) => void; + onMaxClick?: () => void; + className?: string; +}; + +export function MarginPercentageSlider({ value, onChange, onMaxClick, className }: Props) { + const sliderValue = useMemo(() => { + if (!Number.isFinite(value)) { + return 0; + } + + return Math.min(100, Math.max(0, value)); + }, [value]); + const roundedSliderValue = useMemo(() => clampPercentage(sliderValue), [sliderValue]); + const [inputValue, setInputValue] = useState(() => roundedSliderValue.toString()); + + const handleChange = useCallback( + (newValue: number) => { + onChange(clampPercentage(newValue)); + }, + [onChange] + ); + + const handleInputChange = useCallback( + (event: ChangeEvent) => { + const rawValue = event.target.value.replace(/[^\d]/g, "").slice(0, 3); + + if (rawValue === "") { + setInputValue(""); + return; + } + + const nextValue = clampPercentage(Number(rawValue)); + + setInputValue(nextValue.toString()); + onChange(nextValue); + }, + [onChange] + ); + + const handleInputBlur = useCallback(() => { + if (inputValue === "") { + setInputValue(roundedSliderValue.toString()); + return; + } + + const nextValue = clampPercentage(Number(inputValue)); + + setInputValue(nextValue.toString()); + + if (nextValue !== roundedSliderValue) { + onChange(nextValue); + } + }, [inputValue, onChange, roundedSliderValue]); + + const marks = useMemo(() => { + return PERCENTAGE_MARKS.reduce( + (acc, mark) => { + acc[mark] = { + label: `${mark}%`, + }; + return acc; + }, + {} as { [key: number]: { label: string } } + ); + }, []); + + useEffect(() => { + setInputValue(roundedSliderValue.toString()); + }, [roundedSliderValue]); + + return ( +
    +
    + +
    +
    +
    + + % +
    + + +
    +
    + ); +} diff --git a/src/components/TradeboxMarginFields/MarginToPayField.tsx b/src/components/TradeboxMarginFields/MarginToPayField.tsx new file mode 100644 index 0000000000..ef139fb4b2 --- /dev/null +++ b/src/components/TradeboxMarginFields/MarginToPayField.tsx @@ -0,0 +1,146 @@ +import { t } from "@lingui/macro"; +import { useConnectModal } from "@rainbow-me/rainbowkit"; +import cx from "classnames"; +import { ChangeEvent, useCallback, useRef } from "react"; + +import { GMX_ACCOUNT_PSEUDO_CHAIN_ID, SourceChainId } from "config/chains"; +import { isSettlementChain } from "config/multichain"; +import { useTokensData } from "context/SyntheticsStateContext/hooks/globalsHooks"; +import { selectChainId, selectSrcChainId } from "context/SyntheticsStateContext/selectors/globalSelectors"; +import { + selectTradeboxAvailableTokensOptions, + selectTradeboxFromToken, + selectTradeboxState, +} from "context/SyntheticsStateContext/selectors/tradeboxSelectors"; +import { useSelector } from "context/SyntheticsStateContext/utils"; +import { convertToUsd } from "domain/synthetics/tokens"; +import { MissedCoinsPlace } from "domain/synthetics/userFeedback"; +import { formatUsd, parseValue } from "lib/numbers"; +import { useWalletIconUrls } from "lib/wallets/getWalletIconUrls"; +import { useIsNonEoaAccountOnAnyChain } from "lib/wallets/useAccountType"; +import useWallet from "lib/wallets/useWallet"; + +import { useMultichainTradeTokensRequest } from "components/GmxAccountModal/hooks"; +import NumberInput from "components/NumberInput/NumberInput"; +import { MultichainTokenSelector } from "components/TokenSelector/MultichainTokenSelector"; +import TokenSelector from "components/TokenSelector/TokenSelector"; +import TooltipWithPortal from "components/Tooltip/TooltipWithPortal"; + +type Props = { + inputValue: string; + onInputValueChange: (e: ChangeEvent) => void; + onSelectFromTokenAddress: (tokenAddress: string, isGmxAccount: boolean) => void; + onDepositTokenAddress: (tokenAddress: string, chainId: SourceChainId) => void; + onFocus?: () => void; + qa?: string; +}; + +export function MarginToPayField({ + inputValue, + onInputValueChange, + onSelectFromTokenAddress, + onDepositTokenAddress, + onFocus, + qa, +}: Props) { + const inputRef = useRef(null); + + const chainId = useSelector(selectChainId); + const srcChainId = useSelector(selectSrcChainId); + const tokensData = useTokensData(); + const { active, account } = useWallet(); + const { openConnectModal } = useConnectModal(); + const walletIconUrls = useWalletIconUrls(); + const isNonEoaAccountOnAnyChain = useIsNonEoaAccountOnAnyChain(); + + const { tokenChainDataArray: multichainTokens } = useMultichainTradeTokensRequest(chainId, account); + + const availableTokenOptions = useSelector(selectTradeboxAvailableTokensOptions); + const { swapTokens, infoTokens, sortedLongAndShortTokens } = availableTokenOptions; + + const { fromTokenAddress, isFromTokenGmxAccount } = useSelector(selectTradeboxState); + + const fromToken = useSelector(selectTradeboxFromToken); + const fromTokenAmount = fromToken ? parseValue(inputValue || "0", fromToken.decimals)! : 0n; + const fromTokenPrice = fromToken?.prices.minPrice; + const fromUsd = convertToUsd(fromTokenAmount, fromToken?.decimals, fromTokenPrice); + + const handleBoxClick = useCallback((e: React.MouseEvent) => { + // Don't focus input if clicking on the token selector area + if ((e.target as HTMLElement).closest("[data-token-selector]")) { + return; + } + inputRef.current?.focus(); + }, []); + + return ( +
    +
    +
    {t`Margin to Pay`}
    + +
    + + } + content={{formatUsd(fromUsd ?? 0n)}} + variant="none" + position="top" + disabled={fromUsd === undefined} + /> + +
    + {fromTokenAddress && + (!isSettlementChain(chainId) || isNonEoaAccountOnAnyChain ? ( + { + onSelectFromTokenAddress(token.address, false); + }} + tokens={swapTokens} + infoTokens={infoTokens} + showSymbolImage={true} + showTokenImgInDropdown={true} + missedCoinsPlace={MissedCoinsPlace.payToken} + extendedSortSequence={sortedLongAndShortTokens} + qa="margin-collateral-selector" + /> + ) : ( + + ))} +
    +
    +
    +
    + ); +} diff --git a/src/components/TradeboxMarginFields/PriceField.tsx b/src/components/TradeboxMarginFields/PriceField.tsx new file mode 100644 index 0000000000..d4a0a0060a --- /dev/null +++ b/src/components/TradeboxMarginFields/PriceField.tsx @@ -0,0 +1,95 @@ +import { Trans } from "@lingui/macro"; +import { ChangeEvent, useMemo } from "react"; + +import { TokenData } from "domain/synthetics/tokens"; +import { formatAmount, parseValue, USD_DECIMALS } from "lib/numbers"; + +import { TradeInputField } from "./TradeInputField"; + +const ZERO_ALTERNATE_VALUE = "0"; +const DEFAULT_TOKEN_DISPLAY_DECIMALS = 4; +const DEFAULT_TOKEN_DECIMALS = 18; + +type Props = { + indexToken: TokenData | undefined; + markPrice: bigint | undefined; + inputValue: string; + onInputValueChange: (e: ChangeEvent) => void; + onFocus?: () => void; + qa?: string; +}; + +export function PriceField({ indexToken, markPrice, inputValue, onInputValueChange, onFocus, qa }: Props) { + const alternateValue = useMemo( + () => + getAlternatePriceDisplay({ + indexToken, + inputValue, + markPrice, + }), + [indexToken, inputValue, markPrice] + ); + + return ( + Price
    } + alternateValue={alternateValue} + displayMode="usd" + showDisplayModeToggle={false} + unitLabel="USD" + inputValue={inputValue} + onInputValueChange={onInputValueChange} + onFocus={onFocus} + qa={qa} + /> + ); +} + +function getAlternatePriceDisplay({ + indexToken, + inputValue, + markPrice, +}: { + indexToken: TokenData | undefined; + inputValue: string; + markPrice: bigint | undefined; +}) { + if (!inputValue) { + return ZERO_ALTERNATE_VALUE; + } + + const parsedUsd = parseValue(inputValue, USD_DECIMALS); + if (parsedUsd === undefined || parsedUsd === 0n) { + return ZERO_ALTERNATE_VALUE; + } + + if (markPrice === undefined || markPrice === 0n) { + return ZERO_ALTERNATE_VALUE; + } + + const visualMultiplier = BigInt(indexToken?.visualMultiplier ?? 1); + const tokenDecimals = indexToken?.decimals ?? DEFAULT_TOKEN_DECIMALS; + + return formatTokenFromUsdValue({ + parsedInput: parsedUsd, + markPrice, + visualMultiplier, + tokenDecimals, + tokenSymbol: indexToken?.symbol, + tokenPriceDecimals: indexToken?.priceDecimals ?? DEFAULT_TOKEN_DISPLAY_DECIMALS, + }); +} + +function formatTokenFromUsdValue(p: { + parsedInput: bigint; + markPrice: bigint; + visualMultiplier: bigint; + tokenDecimals: number; + tokenSymbol?: string; + tokenPriceDecimals: number; +}) { + const tokenValue = (p.parsedInput * p.visualMultiplier * 10n ** BigInt(p.tokenDecimals)) / p.markPrice; + const formattedValue = formatAmount(tokenValue, p.tokenDecimals, p.tokenPriceDecimals); + + return p.tokenSymbol ? `${formattedValue} ${p.tokenSymbol}` : formattedValue; +} diff --git a/src/components/TradeboxMarginFields/SizeField.tsx b/src/components/TradeboxMarginFields/SizeField.tsx new file mode 100644 index 0000000000..df98005da9 --- /dev/null +++ b/src/components/TradeboxMarginFields/SizeField.tsx @@ -0,0 +1,57 @@ +import { Trans } from "@lingui/macro"; +import { ChangeEvent, useMemo } from "react"; + +import { TokenData } from "domain/synthetics/tokens"; +import { formatAmount, formatUsd } from "lib/numbers"; + +import { TradeInputField, DisplayMode } from "./TradeInputField"; + +export type SizeDisplayMode = DisplayMode; + +type Props = { + sizeInTokens: bigint | undefined; + sizeInUsd: bigint | undefined; + indexToken: TokenData | undefined; + displayMode: SizeDisplayMode; + onDisplayModeChange: (mode: SizeDisplayMode) => void; + inputValue: string; + onInputValueChange: (e: ChangeEvent) => void; + onFocus?: () => void; + qa?: string; +}; + +export function SizeField({ + sizeInTokens, + sizeInUsd, + indexToken, + displayMode, + onDisplayModeChange, + inputValue, + onInputValueChange, + onFocus, + qa, +}: Props) { + const alternateValue = useMemo(() => { + if (displayMode === "token") { + return formatUsd(sizeInUsd ?? 0n, { fallbackToZero: true }); + } else { + if (sizeInTokens === undefined || !indexToken) return "0"; + const visualMultiplier = BigInt(indexToken.visualMultiplier ?? 1); + return `${formatAmount(sizeInTokens / visualMultiplier, indexToken.decimals)} ${indexToken.symbol}`; + } + }, [displayMode, sizeInUsd, sizeInTokens, indexToken]); + + return ( + Size} + alternateValue={alternateValue} + tokenSymbol={indexToken?.symbol} + displayMode={displayMode} + onDisplayModeChange={onDisplayModeChange} + inputValue={inputValue} + onInputValueChange={onInputValueChange} + onFocus={onFocus} + qa={qa} + /> + ); +} diff --git a/src/components/TradeboxMarginFields/TradeInputField.tsx b/src/components/TradeboxMarginFields/TradeInputField.tsx new file mode 100644 index 0000000000..ca45cee0fb --- /dev/null +++ b/src/components/TradeboxMarginFields/TradeInputField.tsx @@ -0,0 +1,112 @@ +import cx from "classnames"; +import { ChangeEvent, useCallback, useRef, ReactNode } from "react"; + +import NumberInput from "components/NumberInput/NumberInput"; + +export type DisplayMode = "token" | "usd"; + +type Props = { + label: ReactNode; + alternateValue: string | undefined; + tokenSymbol?: string; + displayMode: DisplayMode; + onDisplayModeChange?: (mode: DisplayMode) => void; + showDisplayModeToggle?: boolean; + unitLabel?: ReactNode; + inputValue: string; + onInputValueChange: (e: ChangeEvent) => void; + onFocus?: () => void; + placeholder?: string; + qa?: string; +}; + +export function TradeInputField({ + label, + alternateValue, + tokenSymbol, + displayMode, + onDisplayModeChange, + showDisplayModeToggle = true, + unitLabel, + inputValue, + onInputValueChange, + onFocus, + placeholder = "0.0", + qa, +}: Props) { + const inputRef = useRef(null); + + const handleBoxClick = useCallback((e: React.MouseEvent) => { + // Don't focus input if clicking on the toggle area + if ((e.target as HTMLElement).closest("[data-toggle-selector]")) { + return; + } + inputRef.current?.focus(); + }, []); + + return ( +
    +
    +
    +
    {label}
    +
    {alternateValue}
    +
    + +
    +
    + +
    + + {showDisplayModeToggle ? ( +
    + + +
    + ) : unitLabel ? ( +
    {unitLabel}
    + ) : null} +
    +
    +
    + ); +} diff --git a/src/components/TradeboxMarginFields/TradeboxMarginFields.tsx b/src/components/TradeboxMarginFields/TradeboxMarginFields.tsx new file mode 100644 index 0000000000..617ceb48c0 --- /dev/null +++ b/src/components/TradeboxMarginFields/TradeboxMarginFields.tsx @@ -0,0 +1,243 @@ +import { ChangeEvent, useCallback, useEffect, useMemo, useState } from "react"; + +import { SourceChainId } from "config/chains"; +import { useTokensData } from "context/SyntheticsStateContext/hooks/globalsHooks"; +import { selectGasPaymentToken } from "context/SyntheticsStateContext/selectors/expressSelectors"; +import { + selectTradeboxFromToken, + selectTradeboxFocusedInput, + selectTradeboxIncreasePositionAmounts, + selectTradeboxMarkPrice, + selectTradeboxState, + selectTradeboxTradeFlags, +} from "context/SyntheticsStateContext/selectors/tradeboxSelectors"; +import { useSelector } from "context/SyntheticsStateContext/utils"; +import { getMinResidualGasPaymentTokenAmount } from "domain/synthetics/express/getMinResidualGasPaymentTokenAmount"; +import { useMaxAvailableAmount } from "domain/tokens/useMaxAvailableAmount"; +import { calculateDisplayDecimals, formatAmountFree, parseValue, USD_DECIMALS } from "lib/numbers"; +import { getByKey } from "lib/objects"; +import { NATIVE_TOKEN_ADDRESS } from "sdk/configs/tokens"; +import { bigMath } from "sdk/utils/bigmath"; + +import { MarginPercentageSlider } from "./MarginPercentageSlider"; +import { MarginToPayField } from "./MarginToPayField"; +import { PriceField } from "./PriceField"; +import { SizeField, SizeDisplayMode } from "./SizeField"; +import { useSizeConversion } from "./useSizeConversion"; + +type Props = { + onSelectFromTokenAddress: (tokenAddress: string, isGmxAccount: boolean) => void; + onDepositTokenAddress: (tokenAddress: string, chainId: SourceChainId) => void; + fromTokenInputValue: string; + setFromTokenInputValue: (value: string, resetPriceImpact?: boolean) => void; + setFocusedInput: (input: "from" | "to") => void; + toTokenInputValue: string; + setToTokenInputValue: (value: string, resetPriceImpact: boolean) => void; + expressOrdersEnabled: boolean; + gasPaymentTokenAmountForMax?: bigint; + isGasPaymentTokenAmountForMaxApproximate?: boolean; + isExpressLoading?: boolean; + triggerPriceInputValue?: string; + onTriggerPriceInputChange?: (e: ChangeEvent) => void; +}; + +export function TradeboxMarginFields({ + onSelectFromTokenAddress, + onDepositTokenAddress, + fromTokenInputValue, + setFromTokenInputValue, + setFocusedInput, + toTokenInputValue, + setToTokenInputValue, + expressOrdersEnabled, + gasPaymentTokenAmountForMax, + isGasPaymentTokenAmountForMaxApproximate, + isExpressLoading, + triggerPriceInputValue, + onTriggerPriceInputChange, +}: Props) { + const tokensData = useTokensData(); + + const fromToken = useSelector(selectTradeboxFromToken); + const increaseAmounts = useSelector(selectTradeboxIncreasePositionAmounts); + const gasPaymentTokenData = useSelector(selectGasPaymentToken); + const tradeFlags = useSelector(selectTradeboxTradeFlags); + const markPrice = useSelector(selectTradeboxMarkPrice); + const focusedInput = useSelector(selectTradeboxFocusedInput); + + const { fromTokenAddress, toTokenAddress } = useSelector(selectTradeboxState); + + const nativeToken = getByKey(tokensData, NATIVE_TOKEN_ADDRESS); + const toToken = getByKey(tokensData, toTokenAddress); + + const fromTokenAmount = fromToken ? parseValue(fromTokenInputValue || "0", fromToken.decimals)! : 0n; + + const [sizeDisplayMode, setSizeDisplayMode] = useState("token"); + const [sizeInputValue, setSizeInputValue] = useState(toTokenInputValue); + + const { isLimit } = tradeFlags; + const showPriceField = isLimit && triggerPriceInputValue !== undefined; + const sizeFieldInputValue = sizeDisplayMode === "usd" ? sizeInputValue : toTokenInputValue; + + const sizeUsdDisplayDecimals = useMemo( + () => calculateDisplayDecimals(markPrice, USD_DECIMALS, toToken?.visualMultiplier), + [markPrice, toToken?.visualMultiplier] + ); + + const { tokensToUsd, usdToTokens, canConvert } = useSizeConversion({ + toToken, + markPrice, + sizeUsdDisplayDecimals, + }); + + const marginPercentage = useMemo(() => { + if (fromToken?.balance === undefined || fromToken.balance === 0n) return 0; + + const inputAmount = parseValue(fromTokenInputValue || "0", fromToken.decimals) ?? 0n; + if (inputAmount === 0n) return 0; + + const percentage = Number(bigMath.divRound(inputAmount * 100n, fromToken.balance)); + return Math.min(100, Math.max(0, percentage)); + }, [fromTokenInputValue, fromToken?.balance, fromToken?.decimals]); + + useEffect(() => { + if (sizeDisplayMode !== "usd" || !canConvert) return; + + if (focusedInput === "to") { + const tokensValue = usdToTokens(sizeInputValue); + if (tokensValue !== toTokenInputValue) { + setToTokenInputValue(tokensValue, false); + } + } else { + const usdValue = tokensToUsd(toTokenInputValue); + if (usdValue !== sizeInputValue) { + setSizeInputValue(usdValue); + } + } + }, [ + focusedInput, + sizeDisplayMode, + sizeInputValue, + toTokenInputValue, + canConvert, + tokensToUsd, + usdToTokens, + setToTokenInputValue, + ]); + + const { formattedMaxAvailableAmount, showClickMax } = useMaxAvailableAmount({ + fromToken, + nativeToken, + fromTokenAmount, + fromTokenInputValue, + tokenBalanceType: fromToken?.balanceType, + minResidualAmount: getMinResidualGasPaymentTokenAmount({ + gasPaymentToken: gasPaymentTokenData, + gasPaymentTokenAmount: gasPaymentTokenAmountForMax, + payTokenAddress: fromTokenAddress, + applyBuffer: !isGasPaymentTokenAmountForMaxApproximate, + }), + isLoading: expressOrdersEnabled && (isExpressLoading || gasPaymentTokenAmountForMax === undefined), + }); + + const handleFromInputChange = useCallback( + (e: ChangeEvent) => { + setFocusedInput("from"); + setFromTokenInputValue(e.target.value, true); + }, + [setFocusedInput, setFromTokenInputValue] + ); + + const handleMaxClick = useCallback(() => { + if (formattedMaxAvailableAmount) { + setFocusedInput("from"); + setFromTokenInputValue(formattedMaxAvailableAmount, true); + } + }, [formattedMaxAvailableAmount, setFocusedInput, setFromTokenInputValue]); + + const handlePercentageChange = useCallback( + (percentage: number) => { + if (fromToken?.balance === undefined || fromToken.balance === 0n) return; + + const amount = (fromToken.balance * BigInt(percentage)) / 100n; + const formatted = formatAmountFree(amount, fromToken.decimals); + setFocusedInput("from"); + setFromTokenInputValue(formatted, true); + }, + [fromToken?.balance, fromToken?.decimals, setFocusedInput, setFromTokenInputValue] + ); + + const handleSizeInputChange = useCallback( + (e: ChangeEvent) => { + setFocusedInput("to"); + const nextValue = e.target.value; + + if (sizeDisplayMode === "token") { + setToTokenInputValue(nextValue, true); + } else { + setSizeInputValue(nextValue); + } + }, + [sizeDisplayMode, setFocusedInput, setToTokenInputValue] + ); + + const handleSizeDisplayModeChange = useCallback( + (newMode: SizeDisplayMode) => { + if (newMode === sizeDisplayMode) return; + + if (newMode === "token") { + const tokensValue = canConvert ? usdToTokens(sizeInputValue) : toTokenInputValue; + if (tokensValue !== toTokenInputValue) { + setToTokenInputValue(tokensValue, false); + } + setSizeInputValue(tokensValue); + } else { + const usdValue = canConvert ? tokensToUsd(toTokenInputValue) : ""; + setSizeInputValue(usdValue); + } + + setSizeDisplayMode(newMode); + }, + [sizeDisplayMode, canConvert, sizeInputValue, toTokenInputValue, tokensToUsd, usdToTokens, setToTokenInputValue] + ); + + return ( +
    + setFocusedInput("from")} + /> + + setFocusedInput("to")} + qa="position-size" + /> + + {showPriceField && onTriggerPriceInputChange && ( + + )} + + +
    + ); +} diff --git a/src/components/TradeboxMarginFields/index.ts b/src/components/TradeboxMarginFields/index.ts new file mode 100644 index 0000000000..aa510e6b54 --- /dev/null +++ b/src/components/TradeboxMarginFields/index.ts @@ -0,0 +1,6 @@ +export { TradeboxMarginFields } from "./TradeboxMarginFields"; +export { MarginToPayField } from "./MarginToPayField"; +export { MarginPercentageSlider } from "./MarginPercentageSlider"; +export { SizeField, type SizeDisplayMode } from "./SizeField"; +export { PriceField } from "./PriceField"; +export { TradeInputField, type DisplayMode } from "./TradeInputField"; diff --git a/src/components/TradeboxMarginFields/useSizeConversion.ts b/src/components/TradeboxMarginFields/useSizeConversion.ts new file mode 100644 index 0000000000..5581dcc120 --- /dev/null +++ b/src/components/TradeboxMarginFields/useSizeConversion.ts @@ -0,0 +1,51 @@ +import { useCallback, useMemo } from "react"; + +import { TokenData } from "domain/synthetics/tokens"; +import { formatAmount, formatAmountFree, parseValue, USD_DECIMALS } from "lib/numbers"; + +type UseSizeConversionParams = { + toToken: TokenData | undefined; + markPrice: bigint | undefined; + sizeUsdDisplayDecimals: number; +}; + +export function useSizeConversion({ toToken, markPrice, sizeUsdDisplayDecimals }: UseSizeConversionParams) { + const canConvert = toToken !== undefined && markPrice !== undefined && markPrice !== 0n; + const visualMultiplier = BigInt(toToken?.visualMultiplier ?? 1); + const decimals = toToken?.decimals ?? 18; + + const tokensToUsd = useCallback( + (tokensValue: string): string => { + if (!canConvert) return ""; + + const parsedTokens = parseValue(tokensValue, decimals); + if (parsedTokens === undefined) return ""; + + const sizeInUsd = (parsedTokens * visualMultiplier * markPrice) / 10n ** BigInt(decimals); + return formatAmount(sizeInUsd, USD_DECIMALS, sizeUsdDisplayDecimals); + }, + [canConvert, decimals, markPrice, sizeUsdDisplayDecimals, visualMultiplier] + ); + + const usdToTokens = useCallback( + (usdValue: string): string => { + if (!canConvert) return ""; + + const parsedUsd = parseValue(usdValue || "0", USD_DECIMALS); + if (parsedUsd === undefined) return ""; + + const sizeInTokens = (parsedUsd * 10n ** BigInt(decimals)) / markPrice; + return formatAmountFree(sizeInTokens / visualMultiplier, decimals); + }, + [canConvert, decimals, markPrice, visualMultiplier] + ); + + return useMemo( + () => ({ + tokensToUsd, + usdToTokens, + canConvert, + }), + [tokensToUsd, usdToTokens, canConvert] + ); +} diff --git a/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx b/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx index 4bbd498add..24a41887dd 100644 --- a/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx +++ b/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx @@ -53,7 +53,11 @@ import { } from "domain/synthetics/tokens"; import { ConfirmationBoxState, useConfirmationBoxState } from "domain/synthetics/trade/useConfirmationBoxState"; import { PositionEditorState, usePositionEditorState } from "domain/synthetics/trade/usePositionEditorState"; -import { PositionSellerState, usePositionSellerState } from "domain/synthetics/trade/usePositionSellerState"; +import { + OrderOption, + PositionSellerState, + usePositionSellerState, +} from "domain/synthetics/trade/usePositionSellerState"; import { TradeboxState, useTradeboxState } from "domain/synthetics/trade/useTradeboxState"; import useIsFirstOrder from "domain/synthetics/tradeHistory/useIsFirstOrder"; import { MissedCoinsPlace } from "domain/synthetics/userFeedback"; @@ -110,7 +114,8 @@ export type SyntheticsState = { botanixStakingAssetsPerShare: bigint | undefined; closingPositionKey: string | undefined; - setClosingPositionKey: (key: string | undefined) => void; + setClosingPositionKey: (key: string | undefined, orderOption?: OrderOption) => void; + closingPositionOrderOption: OrderOption | undefined; keepLeverage: boolean | undefined; setKeepLeverage: (value: boolean) => void; @@ -232,7 +237,17 @@ export function SyntheticsStateContextProvider({ const { positionsConstants } = usePositionsConstantsRequest(chainId); const { uiFeeFactor } = useUiFeeFactorRequest(chainId); const userReferralInfo = useUserReferralInfoRequest(signer, chainId, account, skipLocalReferralCode); - const [closingPositionKey, setClosingPositionKey] = useState(); + const [closingPositionKeyRaw, setClosingPositionKeyRaw] = useState(); + const [closingPositionOrderOption, setClosingPositionOrderOption] = useState(); + + const closingPositionKey = closingPositionKeyRaw; + const setClosingPositionKey = useMemo( + () => (key: string | undefined, orderOption?: OrderOption) => { + setClosingPositionKeyRaw(key); + setClosingPositionOrderOption(orderOption); + }, + [] + ); const [isCandlesLoaded, setIsCandlesLoaded] = useState(false); const { accruedPositionPriceImpactFees, claimablePositionPriceImpactFees } = useRebatesInfoRequest(chainId, { enabled: isTradePage, @@ -304,8 +319,11 @@ export function SyntheticsStateContextProvider({ const { blockTimestampData } = useBlockTimestampRequest(chainId, { skip: !["trade", "pools"].includes(pageType) }); - // TODO move closingPositionKey to positionSellerState - const positionSellerState = usePositionSellerState(chainId, positionsInfoData?.[closingPositionKey ?? ""]); + const positionSellerState = usePositionSellerState( + chainId, + positionsInfoData?.[closingPositionKey ?? ""], + closingPositionOrderOption + ); const positionEditorState = usePositionEditorState(chainId, srcChainId); const confirmationBoxState = useConfirmationBoxState(); @@ -375,6 +393,8 @@ export function SyntheticsStateContextProvider({ closingPositionKey, setClosingPositionKey, + closingPositionOrderOption, + missedCoinsModalPlace, setMissedCoinsModalPlace, @@ -416,6 +436,26 @@ export function SyntheticsStateContextProvider({ }, [ pageType, chainId, + claimablePositionPriceImpactFees, + closingPositionKey, + closingPositionOrderOption, + confirmationBoxState, + depositMarketTokensData, + externalSwapState, + features, + gasLimits, + gasPaymentTokenAllowance, + gasPrice, + glvInfo, + isCandlesLoaded, + isFirstOrder, + isLargeAccount, + isLoading, + keepLeverage, + l1ExpressOrderGasReference, + lastMonthAccountStats, + lastWeekAccountStats, + leaderboard, srcChainId, account, signer, @@ -423,47 +463,29 @@ export function SyntheticsStateContextProvider({ marketsInfo, ordersInfo, positionsConstants, - glvInfo, botanixStakingAssetsPerShare, - isLoading, positionsInfoData, + progressiveDepositMarketTokensData, + setClosingPositionKey, + setKeepLeverage, + settings, + sponsoredCallBalanceData, + subaccountState, + tokenPermitsState, tokensDataResult, uiFeeFactor, userReferralInfo, - depositMarketTokensData, - progressiveDepositMarketTokensData, multichainMarketTokensBalancesResult, - closingPositionKey, missedCoinsModalPlace, - gasLimits, - gasPrice, - keepLeverage, - setKeepLeverage, - lastWeekAccountStats, - lastMonthAccountStats, accountStats, - isCandlesLoaded, - isLargeAccount, - isFirstOrder, blockTimestampData, oracleSettings, accruedPositionPriceImpactFees, - claimablePositionPriceImpactFees, - leaderboard, - settings, - subaccountState, tradeboxState, - externalSwapState, - tokenPermitsState, orderEditor, positionSellerState, positionEditorState, - confirmationBoxState, poolsDetailsState, - features, - sponsoredCallBalanceData, - gasPaymentTokenAllowance, - l1ExpressOrderGasReference, ]); latestStateRef.current = state; diff --git a/src/context/SyntheticsStateContext/selectors/positionSellerSelectors.ts b/src/context/SyntheticsStateContext/selectors/positionSellerSelectors.ts index 14f957cdc4..8b61d53931 100644 --- a/src/context/SyntheticsStateContext/selectors/positionSellerSelectors.ts +++ b/src/context/SyntheticsStateContext/selectors/positionSellerSelectors.ts @@ -101,7 +101,7 @@ const selectPositionSellerDecreaseAmountArgs = createSelector((q) => { fixedAcceptablePriceImpactBps: selectedTriggerAcceptablePriceImpactBps, marketAddress, positionKey, - tradeMode: orderOption === OrderOption.Market ? TradeMode.Market : TradeMode.Trigger, + tradeMode: orderOption === OrderOption.Twap ? TradeMode.Twap : TradeMode.Market, tradeType, triggerPrice, closeSizeUsd, @@ -187,8 +187,6 @@ export const selectPositionSellerAcceptablePrice = createSelector((q) => { if (orderOption === OrderOption.Market) { return applySlippageToPrice(allowedSlippage, decreaseAmounts.acceptablePrice, false, position.isLong); - } else if (orderOption === OrderOption.Trigger) { - return decreaseAmounts.acceptablePrice; } else if (orderOption === OrderOption.Twap) { return decreaseAmounts.acceptablePrice; } else { @@ -271,15 +269,12 @@ export const selectPositionSellerFees = createSelector((q) => { }); export const selectPositionSellerReceiveToken = createSelector((q) => { - const orderOption = q(selectPositionSellerOrderOption); - const position = q(selectPositionSellerPosition); - const isTrigger = orderOption === OrderOption.Trigger; const isChanged = q(selectPositionSellerReceiveTokenAddressChanged); const defaultReceiveTokenAddress = q(selectPositionSellerDefaultReceiveToken); const receiveTokenAddress = isChanged ? q(selectPositionSellerReceiveTokenAddress) : defaultReceiveTokenAddress ?? q(selectPositionSellerReceiveTokenAddress); - return isTrigger ? position?.collateralToken : q((state) => getByKey(selectTokensData(state), receiveTokenAddress)); + return q((state) => getByKey(selectTokensData(state), receiveTokenAddress)); }); export const selectPositionSellerShouldSwap = createSelector((q) => { diff --git a/src/context/SyntheticsStateContext/selectors/tradeboxSelectors/index.ts b/src/context/SyntheticsStateContext/selectors/tradeboxSelectors/index.ts index 450781cb73..c500b47663 100644 --- a/src/context/SyntheticsStateContext/selectors/tradeboxSelectors/index.ts +++ b/src/context/SyntheticsStateContext/selectors/tradeboxSelectors/index.ts @@ -340,6 +340,13 @@ export const selectTradeBoxTokensAllowanceLoaded = (s: SyntheticsState) => s.tra export const selectTradeboxTwapDuration = (s: SyntheticsState) => s.tradebox.duration; export const selectTradeboxTwapNumberOfParts = (s: SyntheticsState) => s.tradebox.numberOfParts; +export const selectTradeboxIsTPSLEnabled = createSelector((q) => { + const { limitOrTPSL } = q(selectTradeboxAdvancedOptions); + const { isSwap, isTrigger } = q(selectTradeboxTradeFlags); + + return limitOrTPSL && !isSwap && !isTrigger; +}); + export const selectTradeboxIsWrapOrUnwrap = createSelector((q) => { const fromToken = q(selectTradeboxFromToken); const toToken = q(selectTradeboxToToken); diff --git a/src/context/SyntheticsStateContext/selectors/tradeboxSelectors/selectTradeboxSidecarOrders.ts b/src/context/SyntheticsStateContext/selectors/tradeboxSelectors/selectTradeboxSidecarOrders.ts index 20331b38fd..b37c68f32a 100644 --- a/src/context/SyntheticsStateContext/selectors/tradeboxSelectors/selectTradeboxSidecarOrders.ts +++ b/src/context/SyntheticsStateContext/selectors/tradeboxSelectors/selectTradeboxSidecarOrders.ts @@ -1,21 +1,19 @@ import { SyntheticsState } from "context/SyntheticsStateContext/SyntheticsStateContextProvider"; import { createSelectorFactory } from "context/SyntheticsStateContext/utils"; -import { getMarketIndexName, getMarketPoolName } from "domain/synthetics/markets"; import { isLimitDecreaseOrderType, isLimitIncreaseOrderType, isStopLossOrderType, isTwapOrder, } from "domain/synthetics/orders"; -import { getPendingMockPosition } from "domain/synthetics/positions"; import { prepareInitialEntries } from "domain/synthetics/sidecarOrders/utils"; +import { buildTpSlPositionInfo } from "domain/tpsl/sidecar"; import { selectTradeboxCollateralToken, selectTradeboxMarketInfo, selectTradeboxNextPositionValues, selectTradeboxSelectedPositionKey, - selectTradeboxTriggerPrice, } from "."; import { createSelector } from "../../utils"; import { makeSelectOrdersByPositionKey } from "../orderSelectors"; @@ -181,63 +179,27 @@ export const selectTradeboxMockPosition = createSelector((q) => { const tradeFlags = q(selectTradeboxTradeFlags); const nextPositionValues = q(selectTradeboxNextPositionValues); const increaseAmounts = q(selectTradeboxIncreasePositionAmounts); - const triggerPrice = q(selectTradeboxTriggerPrice); if (!positionKey || !marketInfo || !collateralToken || !increaseAmounts || !nextPositionValues) return; - const mockPosition = getPendingMockPosition({ + return buildTpSlPositionInfo({ isIncrease: tradeFlags.isIncrease, positionKey, + marketInfo, + collateralToken, + isLong: tradeFlags.isLong, sizeDeltaUsd: (existingPosition?.sizeInUsd ?? 0n) + (increaseAmounts?.sizeDeltaUsd ?? 0n), sizeDeltaInTokens: (existingPosition?.sizeInTokens ?? 0n) + (increaseAmounts?.sizeDeltaInTokens ?? 0n), collateralDeltaAmount: (existingPosition?.collateralAmount ?? 0n) + (increaseAmounts?.collateralDeltaAmount ?? 0n), - updatedAt: Date.now(), - updatedAtBlock: 0n, - }); - - const indexName = getMarketIndexName(marketInfo); - const poolName = getMarketPoolName(marketInfo); - - if (!mockPosition) return; - - return { - ...mockPosition, - marketInfo, - market: marketInfo, - indexToken: marketInfo.indexToken, - indexName, - poolName, - longToken: marketInfo.longToken, - shortToken: marketInfo.shortToken, - collateralToken, - pnlToken: tradeFlags.isLong ? marketInfo.longToken : marketInfo.shortToken, - markPrice: nextPositionValues.nextEntryPrice!, + markPrice: nextPositionValues.nextEntryPrice, entryPrice: nextPositionValues.nextEntryPrice, - triggerPrice: tradeFlags.isLimit ? triggerPrice : undefined, liquidationPrice: nextPositionValues.nextLiqPrice, - collateralUsd: increaseAmounts?.initialCollateralUsd, - remainingCollateralUsd: increaseAmounts?.collateralDeltaUsd, - remainingCollateralAmount: increaseAmounts?.collateralDeltaAmount, - netValue: increaseAmounts?.collateralDeltaUsd, - hasLowCollateral: false, + collateralUsd: increaseAmounts.initialCollateralUsd, + remainingCollateralUsd: increaseAmounts.collateralDeltaUsd, + remainingCollateralAmount: increaseAmounts.collateralDeltaAmount, + netValue: increaseAmounts.collateralDeltaUsd, leverage: nextPositionValues.nextLeverage, leverageWithPnl: nextPositionValues.nextLeverage, leverageWithoutPnl: nextPositionValues.nextLeverage, - pnl: 0n, - pnlPercentage: 0n, - pnlAfterFees: 0n, - pnlAfterFeesPercentage: 0n, - closingFeeUsd: 0n, - uiFeeUsd: 0n, - pendingFundingFeesUsd: 0n, - pendingClaimableFundingFeesUsd: 0n, - pendingImpactAmount: 0n, - positionFeeAmount: 0n, - netPriceImapctDeltaUsd: 0n, - priceImpactDiffUsd: 0n, - traderDiscountAmount: 0n, - uiFeeAmount: 0n, - pendingImpactUsd: 0n, - closePriceImpactDeltaUsd: 0n, - }; + }); }); diff --git a/src/domain/synthetics/orders/types.ts b/src/domain/synthetics/orders/types.ts index 6b48c37d5c..d9b504d63c 100644 --- a/src/domain/synthetics/orders/types.ts +++ b/src/domain/synthetics/orders/types.ts @@ -18,4 +18,4 @@ export type EditingOrderState = { source: EditingOrderSource; }; -export type EditingOrderSource = "PositionsList" | "PriceChart"; +export type EditingOrderSource = "PositionsList" | "PriceChart" | "TPSLModal"; diff --git a/src/domain/synthetics/sidecarOrders/useSidecarEntries.ts b/src/domain/synthetics/sidecarOrders/useSidecarEntries.ts index a8656450ca..9eadef2573 100644 --- a/src/domain/synthetics/sidecarOrders/useSidecarEntries.ts +++ b/src/domain/synthetics/sidecarOrders/useSidecarEntries.ts @@ -1,12 +1,19 @@ import { useMemo } from "react"; +import { selectTradeboxIsTPSLEnabled } from "context/SyntheticsStateContext/selectors/tradeboxSelectors"; +import { useSelector } from "context/SyntheticsStateContext/utils"; + import { useSidecarOrders } from "./useSidecarOrders"; export function useSidecarEntries() { - const { limit, stopLoss, takeProfit } = useSidecarOrders(); + const { stopLoss, takeProfit } = useSidecarOrders(); + const isTpSlEnabled = useSelector(selectTradeboxIsTPSLEnabled); + + return useMemo(() => { + if (!isTpSlEnabled) { + return []; + } - return useMemo( - () => [...(stopLoss?.entries || []), ...(takeProfit?.entries || []), ...(limit?.entries || [])], - [stopLoss, takeProfit, limit] - ); + return [...(stopLoss?.entries || []), ...(takeProfit?.entries || [])]; + }, [isTpSlEnabled, stopLoss, takeProfit]); } diff --git a/src/domain/synthetics/sidecarOrders/useSidecarOrders.ts b/src/domain/synthetics/sidecarOrders/useSidecarOrders.ts index 2d7ce0b314..225231b9bf 100644 --- a/src/domain/synthetics/sidecarOrders/useSidecarOrders.ts +++ b/src/domain/synthetics/sidecarOrders/useSidecarOrders.ts @@ -5,38 +5,27 @@ import { useUiFeeFactor, useUserReferralInfo, } from "context/SyntheticsStateContext/hooks/globalsHooks"; -import { selectChainId, selectMarketsInfoData } from "context/SyntheticsStateContext/selectors/globalSelectors"; import { selectIsSetAcceptablePriceImpactEnabled } from "context/SyntheticsStateContext/selectors/settingsSelectors"; import { - selectTradeboxCollateralToken, - selectTradeboxFindSwapPath, - selectTradeboxIncreasePositionAmounts, - selectTradeboxMarkPrice, - selectTradeboxMarketInfo, selectTradeboxNextPositionValues, selectTradeboxSelectedPosition, selectTradeboxTradeFlags, selectTradeboxTriggerPrice, + selectTradeboxMarkPrice, } from "context/SyntheticsStateContext/selectors/tradeboxSelectors"; import { selectTradeboxMockPosition, selectTradeboxSidecarEntriesSetIsUntouched, - selectTradeboxSidecarOrdersExistingLimitEntries, - selectTradeboxSidecarOrdersExistingSlEntries, - selectTradeboxSidecarOrdersExistingTpEntries, } from "context/SyntheticsStateContext/selectors/tradeboxSelectors/selectTradeboxSidecarOrders"; -import { selectExternalSwapQuoteParams } from "context/SyntheticsStateContext/selectors/tradeSelectors"; import { useSelector } from "context/SyntheticsStateContext/utils"; import { OrderType } from "domain/synthetics/orders/types"; -import { getDecreasePositionAmounts, getIncreasePositionAmounts } from "domain/synthetics/trade"; +import { getTpSlDecreaseAmounts } from "domain/tpsl/sidecar"; import { usePrevious } from "lib/usePrevious"; -import { convertToTokenAmount } from "../tokens"; -import { SidecarLimitOrderEntry, SidecarOrderEntry, SidecarSlTpOrderEntry, SidecarSlTpOrderEntryValid } from "./types"; +import { SidecarSlTpOrderEntry, SidecarSlTpOrderEntryValid } from "./types"; import { useSidecarOrdersChanged } from "./useSidecarOrdersChanged"; import { useSidecarOrdersGroup } from "./useSidecarOrdersGroup"; import { getCommonError, handleEntryError } from "./utils"; -import { isLimitOrderType } from "../orders"; export * from "./types"; @@ -48,55 +37,11 @@ export function useSidecarOrders() { const isSetAcceptablePriceImpactEnabled = useSelector(selectIsSetAcceptablePriceImpactEnabled); const { isLong, isLimit } = useSelector(selectTradeboxTradeFlags); - const findSwapPath = useSelector(selectTradeboxFindSwapPath); - const marketInfo = useSelector(selectTradeboxMarketInfo); - const collateralToken = useSelector(selectTradeboxCollateralToken); - const increaseAmounts = useSelector(selectTradeboxIncreasePositionAmounts); const triggerPrice = useSelector(selectTradeboxTriggerPrice); const markPrice = useSelector(selectTradeboxMarkPrice); const existingPosition = useSelector(selectTradeboxSelectedPosition); const nextPositionValues = useSelector(selectTradeboxNextPositionValues); - - const existingLimitOrderEntries = useSelector(selectTradeboxSidecarOrdersExistingLimitEntries); - const existingSlOrderEntries = useSelector(selectTradeboxSidecarOrdersExistingSlEntries); - const existingTpOrderEntries = useSelector(selectTradeboxSidecarOrdersExistingTpEntries); - - const handleLimitErrors = useCallback( - (entry: SidecarLimitOrderEntry) => - handleEntryError(entry, "limit", { - liqPrice: nextPositionValues?.nextLiqPrice, - triggerPrice, - isExistingLimits: true, - markPrice, - isLong, - isLimit, - }), - [isLong, isLimit, markPrice, triggerPrice, nextPositionValues] - ); - - const limitEntriesInfo = useSidecarOrdersGroup({ - prefix: "limit", - errorHandler: handleLimitErrors, - initialEntries: existingLimitOrderEntries, - canAddEntry: false, - enablePercentage: false, - }); - - const existingLimits = useMemo(() => { - return limitEntriesInfo.entries.filter((e) => e.txnType !== "cancel"); - }, [limitEntriesInfo.entries]); - - const [minLimitTrigerPrice, maxLimitTrigerPrice] = useMemo(() => { - const prices = existingLimits.reduce((acc, { price }) => (price.value ? [...acc, price.value] : acc), []); - - if (isLimit && triggerPrice !== undefined && triggerPrice !== null) { - prices.push(triggerPrice); - } - - if (!prices.length) return [undefined, undefined]; - - return prices.reduce(([min, max], num) => [num < min ? num : min, num > max ? num : max], [prices[0], prices[0]]); - }, [existingLimits, isLimit, triggerPrice]); + const mockPositionInfo = useSelector(selectTradeboxMockPosition); const handleSLErrors = useCallback( (entry: SidecarSlTpOrderEntry) => @@ -106,22 +51,9 @@ export function useSidecarOrders() { markPrice, isLong, isLimit, - isExistingLimits: !!existingLimits.length, isExistingPosition: !!existingPosition, - maxLimitTrigerPrice, - minLimitTrigerPrice, }), - [ - isLong, - isLimit, - triggerPrice, - markPrice, - existingPosition, - nextPositionValues, - maxLimitTrigerPrice, - minLimitTrigerPrice, - existingLimits, - ] + [isLong, isLimit, triggerPrice, markPrice, existingPosition, nextPositionValues] ); const handleTPErrors = useCallback( @@ -132,139 +64,30 @@ export function useSidecarOrders() { markPrice, isLong, isLimit, - isExistingLimits: !!existingLimits.length, isExistingPosition: !!existingPosition, - maxLimitTrigerPrice, - minLimitTrigerPrice, }), - [ - isLong, - isLimit, - triggerPrice, - markPrice, - existingPosition, - nextPositionValues, - maxLimitTrigerPrice, - minLimitTrigerPrice, - existingLimits, - ] + [isLong, isLimit, triggerPrice, markPrice, existingPosition, nextPositionValues] ); const takeProfitEntriesInfo = useSidecarOrdersGroup({ prefix: "tp", errorHandler: handleTPErrors, - initialEntries: existingTpOrderEntries, + canAddEntry: false, enablePercentage: true, }); + const stopLossEntriesInfo = useSidecarOrdersGroup({ prefix: "sl", errorHandler: handleSLErrors, - initialEntries: existingSlOrderEntries, + canAddEntry: false, enablePercentage: true, }); - const mockPositionInfo = useSelector(selectTradeboxMockPosition); - const marketsInfoData = useSelector(selectMarketsInfoData); - const chainId = useSelector(selectChainId); - const externalSwapQuoteParams = useSelector(selectExternalSwapQuoteParams); - - const getIncreaseAmountsFromEntry = useCallback( - ({ sizeUsd, price, order }: SidecarOrderEntry) => { - if ( - sizeUsd?.value === undefined || - sizeUsd?.value === null || - sizeUsd.error || - price?.value === undefined || - price?.value === null || - price.error - ) - return; - - if (!marketInfo || !mockPositionInfo || !findSwapPath || !order) { - return; - } - - const size = convertToTokenAmount(sizeUsd.value, order.indexToken.decimals, price.value); - - return getIncreasePositionAmounts({ - marketInfo, - indexToken: order.indexToken, - collateralToken: order.targetCollateralToken, - isLong: order.isLong, - initialCollateralToken: order.initialCollateralToken, - initialCollateralAmount: order.initialCollateralDeltaAmount, - indexTokenAmount: size, - triggerPrice: price.value, - position: mockPositionInfo, - externalSwapQuote: undefined, - findSwapPath, - userReferralInfo, - uiFeeFactor, - strategy: "independent", - marketsInfoData, - chainId, - externalSwapQuoteParams, - isSetAcceptablePriceImpactEnabled, - limitOrderType: isLimitOrderType(order.orderType) - ? (order.orderType as OrderType.LimitIncrease | OrderType.StopIncrease) - : undefined, - }); - }, - [ - marketInfo, - mockPositionInfo, - findSwapPath, - uiFeeFactor, - userReferralInfo, - marketsInfoData, - chainId, - externalSwapQuoteParams, - isSetAcceptablePriceImpactEnabled, - ] - ); - - const limit = useMemo(() => { - const entries = limitEntriesInfo.entries.map((entry) => { - return { - ...entry, - increaseAmounts: getIncreaseAmountsFromEntry(entry), - decreaseAmounts: undefined, - }; - }); - - return { - ...limitEntriesInfo, - entries, - totalPnL: 0n, - totalPnLPercentage: 0n, - }; - }, [getIncreaseAmountsFromEntry, limitEntriesInfo]); - - const canCalculatePnL = !existingLimits.length && (!isLimit || !existingPosition); - - const mockPositionInfoWithLimits = useMemo(() => { - if (!mockPositionInfo || !limit.entries.length) return mockPositionInfo; - - const [limitSummaryCollateralDeltaAmount, limitSummarySizeDeltaInTokens, limitSummarySizeDeltaUsd] = - limit.entries.reduce( - ([collateral, tokens, usd], entry) => [ - collateral + (entry.increaseAmounts?.collateralDeltaAmount || 0n), - tokens + (entry.increaseAmounts?.sizeDeltaInTokens || 0n), - usd + (entry.increaseAmounts?.sizeDeltaUsd || 0n), - ], - [0n, 0n, 0n] - ); - - return { - ...mockPositionInfo, - sizeInUsd: (mockPositionInfo?.sizeInUsd ?? 0n) + (limitSummarySizeDeltaUsd ?? 0n), - sizeInTokens: (mockPositionInfo?.sizeInTokens ?? 0n) + (limitSummarySizeDeltaInTokens ?? 0n), - collateralAmount: (mockPositionInfo?.collateralAmount ?? 0n) + (limitSummaryCollateralDeltaAmount ?? 0n), - }; - }, [mockPositionInfo, limit.entries]); - const getDecreaseAmountsFromEntry = useCallback( - ({ sizeUsd, price }: SidecarOrderEntry, triggerOrderType: OrderType.LimitDecrease | OrderType.StopLossDecrease) => { + ( + { sizeUsd, price }: SidecarSlTpOrderEntry, + triggerOrderType: OrderType.LimitDecrease | OrderType.StopLossDecrease + ) => { if ( sizeUsd?.value === undefined || sizeUsd?.value === null || @@ -272,54 +95,41 @@ export function useSidecarOrders() { price?.value === undefined || price?.value === null || price.error || - !marketInfo - ) - return; - - if ( - !increaseAmounts || - !collateralToken || - !mockPositionInfoWithLimits || + !mockPositionInfo || minPositionSizeUsd === undefined || minCollateralUsd === undefined ) { return; } - return getDecreasePositionAmounts({ - marketInfo, - collateralToken, - isLong, - position: mockPositionInfoWithLimits, + return getTpSlDecreaseAmounts({ + position: mockPositionInfo, closeSizeUsd: sizeUsd.value, - keepLeverage: true, triggerPrice: price.value, - userReferralInfo, + triggerOrderType, + isLimit, + limitPrice: triggerPrice, minCollateralUsd, minPositionSizeUsd, uiFeeFactor, - isLimit, - limitPrice: triggerPrice, - triggerOrderType, + userReferralInfo, isSetAcceptablePriceImpactEnabled, }); }, [ - collateralToken, - mockPositionInfoWithLimits, - increaseAmounts, - isLong, isLimit, - marketInfo, - triggerPrice, minCollateralUsd, minPositionSizeUsd, + mockPositionInfo, + triggerPrice, uiFeeFactor, userReferralInfo, isSetAcceptablePriceImpactEnabled, ] ); + const canCalculatePnL = !isLimit || !existingPosition; + const stopLoss = useMemo(() => { const entries = stopLossEntriesInfo.entries.map((entry) => { return { @@ -383,10 +193,9 @@ export function useSidecarOrders() { }, [getDecreaseAmountsFromEntry, takeProfitEntriesInfo, canCalculatePnL]); const reset = useCallback(() => { - limit.reset(); stopLoss.reset(); takeProfit.reset(); - }, [limit, stopLoss, takeProfit]); + }, [stopLoss, takeProfit]); const doesEntriesChanged = useSidecarOrdersChanged(); const prevDoesEntriesChanged = usePrevious(doesEntriesChanged); @@ -394,11 +203,6 @@ export function useSidecarOrders() { useEffect(() => { if (prevDoesEntriesChanged === false && doesEntriesChanged) { reset(); - /** - * We need to reset the untouched state to false, to prevent next init on [./useSidecarOrdersGroup.ts#L115] - * from UI perspective this prevents cursor focus loose without input change - */ - setIsUntouched("limit", false); setIsUntouched("sl", false); setIsUntouched("tp", false); } @@ -407,7 +211,6 @@ export function useSidecarOrders() { return { stopLoss, takeProfit, - limit, reset, }; } diff --git a/src/domain/synthetics/sidecarOrders/useSidecarOrdersGroup.ts b/src/domain/synthetics/sidecarOrders/useSidecarOrdersGroup.ts index c273154aaa..fade6e8eb9 100644 --- a/src/domain/synthetics/sidecarOrders/useSidecarOrdersGroup.ts +++ b/src/domain/synthetics/sidecarOrders/useSidecarOrdersGroup.ts @@ -106,12 +106,8 @@ export function useSidecarOrdersGroup({ }); } - if (canAddEntry) { - return [errorHandler(getDefaultEntry(prefix, { mode: enablePercentage ? "fitPercentage" : "keepSize" }))]; - } - - return []; - }, [initialEntries, prefix, canAddEntry, enablePercentage, errorHandler, recalculateEntryByField]); + return [errorHandler(getDefaultEntry(prefix, { mode: enablePercentage ? "fitPercentage" : "keepSize" }))]; + }, [initialEntries, prefix, enablePercentage, errorHandler, recalculateEntryByField]); const ordersState = useSelector(makeSelectTradeboxSidecarOrdersState(prefix)); diff --git a/src/domain/synthetics/sidecarOrders/utils.ts b/src/domain/synthetics/sidecarOrders/utils.ts index a26c4c8680..a6b6b04188 100644 --- a/src/domain/synthetics/sidecarOrders/utils.ts +++ b/src/domain/synthetics/sidecarOrders/utils.ts @@ -2,7 +2,6 @@ import { t } from "@lingui/macro"; import uniqueId from "lodash/uniqueId"; import { USD_DECIMALS } from "config/factors"; -import { BASIS_POINTS_DIVISOR, MAX_ALLOWED_LEVERAGE } from "config/factors"; import { PositionOrderInfo } from "domain/synthetics/orders"; import { calculateDisplayDecimals, formatAmount, parseValue, removeTrailingZeros } from "lib/numbers"; @@ -96,27 +95,20 @@ export function prepareInitialEntries({ export function handleEntryError( entry: T, - type: "sl" | "tp" | "limit", + type: "sl" | "tp", { - liqPrice, triggerPrice, markPrice, isLong, isLimit, - isExistingLimits, isExistingPosition, - maxLimitTrigerPrice, - minLimitTrigerPrice, }: { liqPrice?: bigint; triggerPrice?: bigint; markPrice?: bigint; isLong?: boolean; isLimit?: boolean; - isExistingLimits?: boolean; isExistingPosition?: boolean; - maxLimitTrigerPrice?: bigint; - minLimitTrigerPrice?: bigint; } ): T { let sizeError: string | null = null; @@ -126,26 +118,6 @@ export function handleEntryError( const inputPrice = entry.price.value; if (inputPrice !== undefined && inputPrice !== null && inputPrice > 0) { - if (markPrice !== undefined) { - if (type === "limit") { - const nextError = isLong - ? inputPrice > markPrice && t`Limit price above mark price` - : inputPrice < markPrice && t`Limit price below mark price`; - - priceError = nextError || priceError; - } - } - - if (!isExistingLimits && liqPrice !== undefined && liqPrice !== null) { - if (type === "sl") { - const nextError = isLong - ? inputPrice < liqPrice && t`Trigger price below liq. price` - : inputPrice > liqPrice && t`Trigger price above liq. price`; - - priceError = nextError || priceError; - } - } - if (isExistingPosition || !isLimit) { if (markPrice !== undefined && markPrice !== null) { if (type === "tp") { @@ -165,63 +137,28 @@ export function handleEntryError( } } } else { - if (isExistingLimits) { - if ( - maxLimitTrigerPrice !== undefined && - maxLimitTrigerPrice !== null && - minLimitTrigerPrice !== undefined && - minLimitTrigerPrice !== null - ) { - if (type === "tp") { - const nextError = isLong - ? inputPrice < maxLimitTrigerPrice && t`Trigger price below highest limit price` - : inputPrice > minLimitTrigerPrice && t`Trigger price above lowest limit price`; - - priceError = nextError || priceError; - } - - if (type === "sl") { - const nextError = isLong - ? inputPrice > maxLimitTrigerPrice && t`Trigger price above highest limit price` - : inputPrice < minLimitTrigerPrice && t`Trigger price below lowest limit price`; - - priceError = nextError || priceError; - } + if (triggerPrice !== undefined && triggerPrice !== null) { + if (type === "tp") { + const nextError = isLong + ? inputPrice < triggerPrice && t`Trigger price below limit price` + : inputPrice > triggerPrice && t`Trigger price above limit price`; + + priceError = nextError || priceError; } - } else { - if (triggerPrice !== undefined && triggerPrice !== null) { - if (type === "tp") { - const nextError = isLong - ? inputPrice < triggerPrice && t`Trigger price below limit price` - : inputPrice > triggerPrice && t`Trigger price above limit price`; - - priceError = nextError || priceError; - } - - if (type === "sl") { - const nextError = isLong - ? inputPrice > triggerPrice && t`Trigger price above limit price` - : inputPrice < triggerPrice && t`Trigger price below limit price`; - - priceError = nextError || priceError; - } + + if (type === "sl") { + const nextError = isLong + ? inputPrice > triggerPrice && t`Trigger price above limit price` + : inputPrice < triggerPrice && t`Trigger price below limit price`; + + priceError = nextError || priceError; } } } } - if (type === "limit") { - if (entry.sizeUsd?.value === undefined || entry.sizeUsd.value === 0n) { - sizeError = t`Limit size is required`; - } - - if (entry?.increaseAmounts?.estimatedLeverage && entry?.increaseAmounts?.estimatedLeverage > MAX_ALLOWED_LEVERAGE) { - sizeError = t`Max leverage: ${(MAX_ALLOWED_LEVERAGE / BASIS_POINTS_DIVISOR).toFixed(1)}x`; - } - } else { - if (entry.percentage?.value === undefined || entry.percentage?.value === 0n) { - percentageError = t`A size percentage is required`; - } + if (entry.percentage?.value === undefined || entry.percentage?.value === 0n) { + percentageError = t`A size percentage is required`; } return { diff --git a/src/domain/synthetics/trade/useMaxAutoCancelOrdersState.tsx b/src/domain/synthetics/trade/useMaxAutoCancelOrdersState.tsx index 6c6578f8a8..20d915f81c 100644 --- a/src/domain/synthetics/trade/useMaxAutoCancelOrdersState.tsx +++ b/src/domain/synthetics/trade/useMaxAutoCancelOrdersState.tsx @@ -6,7 +6,7 @@ import { selectMaxAutoCancelOrders } from "context/SyntheticsStateContext/select import { makeSelectOrdersByPositionKey } from "context/SyntheticsStateContext/selectors/orderSelectors"; import { selectTradeboxSelectedPositionKey } from "context/SyntheticsStateContext/selectors/tradeboxSelectors"; import { useSelector } from "context/SyntheticsStateContext/utils"; -import { useSidecarOrders } from "domain/synthetics/sidecarOrders/useSidecarOrders"; +import { useSidecarEntries } from "domain/synthetics/sidecarOrders/useSidecarEntries"; import { AlertInfoCard } from "components/AlertInfo/AlertInfoCard"; import ExternalLink from "components/ExternalLink/ExternalLink"; @@ -20,7 +20,7 @@ export function useMaxAutoCancelOrdersState({ }) { const { isAutoCancelTPSL: isEnabledAutoCancel } = useSettings(); const maxAutoCancelOrders = useSelector(selectMaxAutoCancelOrders); - const { stopLoss, takeProfit } = useSidecarOrders(); + const sidecarEntries = useSidecarEntries(); const positionOrders = useSelector(makeSelectOrdersByPositionKey(positionKey)); const selectedPositionKey = useSelector(selectTradeboxSelectedPositionKey); @@ -28,9 +28,7 @@ export function useMaxAutoCancelOrdersState({ let draftOrdersCount = isCreatingNewAutoCancel ? 1 : 0; if (shouldCountDraftSidecarOrders) { - draftOrdersCount += [...stopLoss.entries, ...takeProfit.entries].filter( - (entry) => entry.txnType === "create" - ).length; + draftOrdersCount += sidecarEntries.filter((entry) => entry.txnType === "create").length; } const existingAutoCancelOrders = useMemo(() => { diff --git a/src/domain/synthetics/trade/usePositionSellerState.ts b/src/domain/synthetics/trade/usePositionSellerState.ts index d4a1035d1c..4839755a65 100644 --- a/src/domain/synthetics/trade/usePositionSellerState.ts +++ b/src/domain/synthetics/trade/usePositionSellerState.ts @@ -1,4 +1,4 @@ -import { useCallback, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { getKeepLeverageKey, getSyntheticsReceiveMoneyTokenKey } from "config/localStorage"; import { useSettings } from "context/SettingsContext/SettingsContextProvider"; @@ -11,21 +11,23 @@ import { PositionInfo } from "../positions"; export enum OrderOption { Market = "Market", - Trigger = "Trigger", Twap = "TWAP", } export const ORDER_OPTION_TO_TRADE_MODE: Record = { [OrderOption.Market]: TradeMode.Market, - [OrderOption.Trigger]: TradeMode.Trigger, [OrderOption.Twap]: TradeMode.Twap, }; export type PositionSellerState = ReturnType; -export function usePositionSellerState(chainId: number, closingPosition: PositionInfo | undefined) { +export function usePositionSellerState( + chainId: number, + closingPosition: PositionInfo | undefined, + initialOrderOption: OrderOption | undefined +) { const { savedAllowedSlippage } = useSettings(); - const [orderOption, setOrderOption] = useState(OrderOption.Market); + const [orderOption, setOrderOption] = useState(initialOrderOption ?? OrderOption.Market); const [triggerPriceInputValue, setTriggerPriceInputValue] = useState(""); const [keepLeverage, setKeepLeverage] = useLocalStorageSerializeKey(getKeepLeverageKey(chainId), true); const [defaultTriggerAcceptablePriceImpactBps, setDefaultTriggerAcceptablePriceImpactBps] = useState(); @@ -38,6 +40,12 @@ export function usePositionSellerState(chainId: number, closingPosition: Positio const [duration, setDuration] = useState(DEFAULT_TWAP_DURATION); const [numberOfParts, setNumberOfParts] = useState(DEFAULT_TWAP_NUMBER_OF_PARTS); + useEffect(() => { + if (initialOrderOption !== undefined) { + setOrderOption(initialOrderOption); + } + }, [initialOrderOption]); + const resetPositionSeller = useCallback(() => { setOrderOption(OrderOption.Market); setTriggerPriceInputValue(""); diff --git a/src/domain/synthetics/trade/useTradeboxState.ts b/src/domain/synthetics/trade/useTradeboxState.ts index b3234e74a5..59efa86f94 100644 --- a/src/domain/synthetics/trade/useTradeboxState.ts +++ b/src/domain/synthetics/trade/useTradeboxState.ts @@ -290,7 +290,6 @@ export function useTradeboxState( const [leverageOption, setLeverageOption] = useLocalStorageSerializeKey(getLeverageKey(chainId), 2); const [keepLeverage, setKeepLeverage] = useLocalStorageSerializeKey(getKeepLeverageKey(chainId), true); - const [leverageInputValue, setLeverageInputValue] = useState(() => leverageOption?.toString() ?? ""); const tradeFlags = useMemo(() => createTradeFlags(tradeType, tradeMode), [tradeType, tradeMode]); const { isSwap } = tradeFlags; @@ -602,41 +601,6 @@ export function useTradeboxState( [setStoredOptions] ); - const handleLeverageInputChange = useCallback( - (value: string) => { - const sanitizedValue = value.replace(",", "."); - - const endsInDot = sanitizedValue.endsWith("."); - - const numberValue = parseFloat(sanitizedValue); - - if (isNaN(numberValue)) { - setLeverageInputValue(value); - return; - } - - const truncatedValue = Math.trunc(numberValue * 10) / 10; - - let stringValue = truncatedValue.toString(); - - if (endsInDot) { - stringValue += "."; - } - - setLeverageInputValue(stringValue); - setLeverageOption(truncatedValue); - }, - [setLeverageOption] - ); - - const handleLeverageSliderChange = useCallback( - (value: number) => { - setLeverageOption(value); - setLeverageInputValue(value.toString()); - }, - [setLeverageOption] - ); - const sidecarOrders = useSidecarOrdersState(); useEffect( @@ -664,7 +628,7 @@ export function useTradeboxState( return; } - if (tradeType && tradeMode && !availableTradeModes.flat().includes(tradeMode)) { + if (tradeType && tradeMode && !availableTradeModes.flat().some((mode) => mode === tradeMode)) { setTradeMode(availableTradeModes[0]); } }, @@ -793,10 +757,8 @@ export function useTradeboxState( setTriggerPriceInputValue, triggerRatioInputValue, setTriggerRatioInputValue, - leverageInputValue, - setLeverageInputValue: handleLeverageInputChange, leverageOption, - setLeverageOption: handleLeverageSliderChange, + setLeverageOption, keepLeverage, setKeepLeverage, advancedOptions, diff --git a/src/domain/synthetics/trade/utils/availableTradeModes.ts b/src/domain/synthetics/trade/utils/availableTradeModes.ts index 3e0ca210d7..04252e87e3 100644 --- a/src/domain/synthetics/trade/utils/availableTradeModes.ts +++ b/src/domain/synthetics/trade/utils/availableTradeModes.ts @@ -4,16 +4,8 @@ import { NATIVE_TOKEN_ADDRESS } from "sdk/configs/tokens"; import { TradeMode, TradeType } from "sdk/types/trade"; const AVAILABLE_TRADE_MODES = { - [TradeType.Long]: [ - TradeMode.Market, - TradeMode.Limit, - [TradeMode.Trigger, TradeMode.StopMarket, TradeMode.Twap], - ] as const, - [TradeType.Short]: [ - TradeMode.Market, - TradeMode.Limit, - [TradeMode.Trigger, TradeMode.StopMarket, TradeMode.Twap], - ] as const, + [TradeType.Long]: [TradeMode.Market, TradeMode.Limit, [TradeMode.StopMarket, TradeMode.Twap]] as const, + [TradeType.Short]: [TradeMode.Market, TradeMode.Limit, [TradeMode.StopMarket, TradeMode.Twap]] as const, [TradeType.Swap]: [TradeMode.Market, TradeMode.Limit, TradeMode.Twap] as const, }; diff --git a/src/domain/synthetics/trade/utils/common.ts b/src/domain/synthetics/trade/utils/common.ts index e57c941868..9987bf553a 100644 --- a/src/domain/synthetics/trade/utils/common.ts +++ b/src/domain/synthetics/trade/utils/common.ts @@ -82,7 +82,7 @@ export function getPositionSellerTradeFlags(isLong: boolean | undefined, orderOp isShort: !isLong, isSwap: false, isPosition: true, - isTrigger: orderOption === OrderOption.Trigger, + isTrigger: false, isTwap: orderOption === OrderOption.Twap, }; } diff --git a/src/domain/tpsl/sidecar.ts b/src/domain/tpsl/sidecar.ts new file mode 100644 index 0000000000..bd05fb38ce --- /dev/null +++ b/src/domain/tpsl/sidecar.ts @@ -0,0 +1,264 @@ +import { UI_FEE_RECEIVER_ACCOUNT } from "config/ui"; +import type { selectUserReferralInfo } from "context/SyntheticsStateContext/selectors/globalSelectors"; +import type { MarketInfo } from "domain/synthetics/markets"; +import { getMarketIndexName, getMarketPoolName } from "domain/synthetics/markets"; +import { DecreasePositionSwapType, OrderType } from "domain/synthetics/orders"; +import type { PositionInfo, PositionInfoLoaded } from "domain/synthetics/positions"; +import { getPendingMockPosition } from "domain/synthetics/positions/usePositions"; +import type { TokenData } from "domain/synthetics/tokens"; +import { getDecreasePositionAmounts } from "domain/synthetics/trade"; +import type { DecreasePositionAmounts } from "domain/synthetics/trade"; +import { buildDecreaseOrderPayload } from "sdk/utils/orderTransactions"; + +export type BuildTpSlPositionInfoParams = { + positionKey?: string; + marketInfo?: MarketInfo; + collateralToken?: TokenData; + isLong?: boolean; + isIncrease: boolean; + sizeDeltaUsd?: bigint; + sizeDeltaInTokens?: bigint; + collateralDeltaAmount?: bigint; + markPrice?: bigint; + entryPrice?: bigint; + liquidationPrice?: bigint; + collateralUsd?: bigint; + remainingCollateralUsd?: bigint; + remainingCollateralAmount?: bigint; + netValue?: bigint; + leverage?: bigint; + leverageWithPnl?: bigint; + leverageWithoutPnl?: bigint; + existingPosition?: PositionInfo; + includeExistingFees?: boolean; + requirePositiveSizes?: boolean; +}; + +export type TpSlInputPositionData = { + sizeInUsd: bigint; + sizeInTokens: bigint; + collateralUsd: bigint; + entryPrice: bigint; + referencePrice?: bigint; + liquidationPrice?: bigint; + isLong: boolean; + indexTokenDecimals: number; + visualMultiplier?: number; +}; + +export function buildTpSlInputPositionData(p: { + position?: Pick< + PositionInfo, + "sizeInUsd" | "sizeInTokens" | "collateralUsd" | "entryPrice" | "liquidationPrice" | "isLong" + >; + collateralUsd?: bigint; + indexTokenDecimals: number; + visualMultiplier?: number; + referencePrice?: bigint; +}): TpSlInputPositionData | undefined { + if (!p.position) { + return undefined; + } + + return { + sizeInUsd: p.position.sizeInUsd ?? 0n, + sizeInTokens: p.position.sizeInTokens ?? 0n, + collateralUsd: p.collateralUsd ?? p.position.collateralUsd ?? 0n, + entryPrice: p.position.entryPrice ?? 0n, + referencePrice: p.referencePrice, + liquidationPrice: p.position.liquidationPrice, + isLong: p.position.isLong, + indexTokenDecimals: p.indexTokenDecimals, + visualMultiplier: p.visualMultiplier, + }; +} + +export function buildTpSlPositionInfo(p: BuildTpSlPositionInfoParams): PositionInfoLoaded | undefined { + if (!p.positionKey || !p.marketInfo || !p.collateralToken) { + return undefined; + } + + const sizeDeltaUsd = p.sizeDeltaUsd ?? 0n; + const sizeDeltaInTokens = p.sizeDeltaInTokens ?? 0n; + const collateralDeltaAmount = p.collateralDeltaAmount ?? 0n; + + if (p.requirePositiveSizes && (sizeDeltaUsd <= 0n || sizeDeltaInTokens <= 0n)) { + return undefined; + } + + const pending = getPendingMockPosition({ + isIncrease: p.isIncrease, + positionKey: p.positionKey, + sizeDeltaUsd, + sizeDeltaInTokens, + collateralDeltaAmount, + updatedAt: Date.now(), + updatedAtBlock: 0n, + }); + + const indexName = getMarketIndexName(p.marketInfo); + const poolName = getMarketPoolName(p.marketInfo); + const feeSource = p.includeExistingFees ? p.existingPosition : undefined; + const isLong = p.isLong ?? pending.isLong; + + const markPrice = p.markPrice ?? feeSource?.markPrice ?? 0n; + const entryPrice = p.entryPrice ?? feeSource?.entryPrice; + const liquidationPrice = p.liquidationPrice ?? feeSource?.liquidationPrice; + + const leverage = p.leverage ?? feeSource?.leverage; + const leverageWithPnl = p.leverageWithPnl ?? feeSource?.leverageWithPnl ?? leverage; + const leverageWithoutPnl = p.leverageWithoutPnl ?? feeSource?.leverageWithoutPnl ?? leverage; + + return { + ...pending, + marketInfo: p.marketInfo, + market: p.marketInfo, + indexToken: p.marketInfo.indexToken, + indexName, + poolName, + longToken: p.marketInfo.longToken, + shortToken: p.marketInfo.shortToken, + collateralToken: p.collateralToken, + pnlToken: isLong ? p.marketInfo.longToken : p.marketInfo.shortToken, + markPrice, + entryPrice, + liquidationPrice, + collateralUsd: p.collateralUsd ?? feeSource?.collateralUsd ?? 0n, + remainingCollateralUsd: p.remainingCollateralUsd ?? feeSource?.remainingCollateralUsd ?? 0n, + remainingCollateralAmount: p.remainingCollateralAmount ?? feeSource?.remainingCollateralAmount ?? 0n, + netValue: p.netValue ?? feeSource?.netValue ?? 0n, + hasLowCollateral: false, + leverage, + leverageWithPnl, + leverageWithoutPnl, + pnl: 0n, + pnlPercentage: 0n, + pnlAfterFees: 0n, + pnlAfterFeesPercentage: 0n, + closingFeeUsd: 0n, + uiFeeUsd: 0n, + pendingFundingFeesUsd: feeSource?.pendingFundingFeesUsd ?? 0n, + pendingBorrowingFeesUsd: feeSource?.pendingBorrowingFeesUsd ?? 0n, + pendingClaimableFundingFeesUsd: feeSource?.pendingClaimableFundingFeesUsd ?? 0n, + pendingImpactAmount: feeSource?.pendingImpactAmount ?? 0n, + positionFeeAmount: feeSource?.positionFeeAmount ?? 0n, + netPriceImapctDeltaUsd: feeSource?.netPriceImapctDeltaUsd ?? 0n, + priceImpactDiffUsd: feeSource?.priceImpactDiffUsd ?? 0n, + traderDiscountAmount: feeSource?.traderDiscountAmount ?? 0n, + uiFeeAmount: feeSource?.uiFeeAmount ?? 0n, + pendingImpactUsd: feeSource?.pendingImpactUsd ?? 0n, + closePriceImpactDeltaUsd: feeSource?.closePriceImpactDeltaUsd ?? 0n, + }; +} + +export function getTpSlDecreaseAmounts(p: { + position: PositionInfoLoaded | undefined; + closeSizeUsd: bigint | null | undefined; + triggerPrice: bigint | null | undefined; + triggerOrderType: OrderType.LimitDecrease | OrderType.StopLossDecrease | undefined; + keepLeverage?: boolean; + isLimit: boolean; + limitPrice: bigint | undefined; + minCollateralUsd: bigint | undefined; + minPositionSizeUsd: bigint | undefined; + uiFeeFactor: bigint | undefined; + userReferralInfo: ReturnType | undefined; + isSetAcceptablePriceImpactEnabled: boolean; +}): DecreasePositionAmounts | undefined { + if ( + !p.position || + p.closeSizeUsd === undefined || + p.closeSizeUsd === null || + p.triggerPrice === undefined || + p.triggerPrice === null || + p.minCollateralUsd === undefined || + p.minPositionSizeUsd === undefined || + p.uiFeeFactor === undefined || + !p.triggerOrderType + ) { + return undefined; + } + + return getDecreasePositionAmounts({ + marketInfo: p.position.marketInfo, + collateralToken: p.position.collateralToken, + isLong: p.position.isLong, + position: p.position, + closeSizeUsd: p.closeSizeUsd, + keepLeverage: p.keepLeverage ?? true, + triggerPrice: p.triggerPrice, + userReferralInfo: p.userReferralInfo, + minCollateralUsd: p.minCollateralUsd, + minPositionSizeUsd: p.minPositionSizeUsd, + uiFeeFactor: p.uiFeeFactor, + isLimit: p.isLimit, + limitPrice: p.limitPrice, + triggerOrderType: p.triggerOrderType, + isSetAcceptablePriceImpactEnabled: p.isSetAcceptablePriceImpactEnabled, + }); +} + +export type TpSlCreatePayloadEntry = { + amounts?: DecreasePositionAmounts; + executionFeeAmount?: bigint; + executionGasLimit?: bigint; +}; + +export function buildTpSlCreatePayloads(p: { + autoCancelOrdersLimit: number; + chainId: number; + account: string | undefined; + marketAddress: string | undefined; + indexTokenAddress: string | undefined; + collateralTokenAddress: string | undefined; + isLong: boolean | undefined; + entries: TpSlCreatePayloadEntry[]; + userReferralCode: string | undefined; +}) { + if ( + !p.account || + !p.marketAddress || + !p.indexTokenAddress || + !p.collateralTokenAddress || + typeof p.isLong !== "boolean" + ) { + return []; + } + + const { account, marketAddress, indexTokenAddress, collateralTokenAddress, isLong } = p; + + const validEntries = p.entries.filter((item): item is TpSlCreatePayloadEntry & { amounts: DecreasePositionAmounts } => + Boolean(item.amounts) + ); + + return validEntries.map((item, i) => { + const amounts = item.amounts; + + return buildDecreaseOrderPayload({ + chainId: p.chainId as any, + receiver: account, + collateralDeltaAmount: amounts.collateralDeltaAmount ?? 0n, + collateralTokenAddress, + sizeDeltaUsd: amounts.sizeDeltaUsd, + sizeDeltaInTokens: amounts.sizeDeltaInTokens, + referralCode: p.userReferralCode, + uiFeeReceiver: UI_FEE_RECEIVER_ACCOUNT, + allowedSlippage: 0, + orderType: amounts.triggerOrderType ?? OrderType.LimitDecrease, + autoCancel: i < p.autoCancelOrdersLimit, + swapPath: [], + externalSwapQuote: undefined, + marketAddress, + indexTokenAddress, + isLong, + acceptablePrice: amounts.acceptablePrice ?? 0n, + triggerPrice: amounts.triggerPrice ?? 0n, + receiveTokenAddress: collateralTokenAddress, + minOutputUsd: 0n, + decreasePositionSwapType: amounts.decreaseSwapType ?? DecreasePositionSwapType.NoSwap, + executionFeeAmount: item.executionFeeAmount ?? 0n, + executionGasLimit: item.executionGasLimit ?? 0n, + validFromTime: 0n, + }); + }); +} diff --git a/src/domain/tpsl/utils.ts b/src/domain/tpsl/utils.ts new file mode 100644 index 0000000000..6f536b6637 --- /dev/null +++ b/src/domain/tpsl/utils.ts @@ -0,0 +1,83 @@ +import type { PositionOrderInfo } from "domain/synthetics/orders"; +import { convertToTokenAmount, convertToUsd } from "domain/synthetics/tokens"; +import type { TokenData } from "domain/synthetics/tokens"; + +export function calculateTotalSizeUsd(p: { + existingPositionSizeUsd: bigint | undefined; + isLimitOrStopIncrease: boolean; + order: PositionOrderInfo | undefined; + sizeDeltaUsd: bigint; +}) { + if (!p.isLimitOrStopIncrease || !p.order) { + return 0n; + } + + return (p.existingPositionSizeUsd ?? 0n) + p.sizeDeltaUsd; +} + +export function calculateTotalSizeInTokens(p: { + baseSizeInTokens: bigint | undefined; + increaseSizeDeltaInTokens: bigint | undefined; + isLimitOrStopIncrease: boolean; + order: PositionOrderInfo | undefined; + positionIndexToken: TokenData | undefined; + sizeDeltaUsd: bigint; + triggerPrice: bigint | undefined; +}) { + if (!p.isLimitOrStopIncrease || !p.order) { + return 0n; + } + + const baseSizeInTokens = p.baseSizeInTokens ?? 0n; + + if (p.increaseSizeDeltaInTokens !== undefined) { + return baseSizeInTokens + p.increaseSizeDeltaInTokens; + } + + if (!p.positionIndexToken || p.triggerPrice === undefined || p.sizeDeltaUsd === 0n) { + return baseSizeInTokens; + } + + const orderSizeInTokens = convertToTokenAmount(p.sizeDeltaUsd, p.positionIndexToken.decimals, p.triggerPrice) ?? 0n; + + return baseSizeInTokens + orderSizeInTokens; +} + +export function getCollateralDeltaAmount(p: { + collateralDeltaAmount: bigint | undefined; + isLimitOrStopIncrease: boolean; + order: PositionOrderInfo | undefined; +}) { + if (!p.isLimitOrStopIncrease || !p.order) { + return 0n; + } + + if (p.collateralDeltaAmount !== undefined) { + return p.collateralDeltaAmount; + } + + return p.order.initialCollateralDeltaAmount ?? 0n; +} + +export function getCollateralDeltaUsd(p: { + collateralDeltaAmount: bigint; + collateralDeltaUsd: bigint | undefined; + isLimitOrStopIncrease: boolean; + order: PositionOrderInfo | undefined; +}) { + if (!p.isLimitOrStopIncrease || !p.order) { + return 0n; + } + + if (p.collateralDeltaUsd !== undefined) { + return p.collateralDeltaUsd; + } + + return ( + convertToUsd( + p.collateralDeltaAmount, + p.order.targetCollateralToken.decimals, + p.order.targetCollateralToken.prices.minPrice + ) ?? 0n + ); +} diff --git a/src/img/ic_buy.svg b/src/img/ic_buy.svg index 4d91d9bf8c..7e0ce5908f 100644 --- a/src/img/ic_buy.svg +++ b/src/img/ic_buy.svg @@ -1,4 +1,6 @@ - - - + + + + diff --git a/src/img/tokens/ic_box_chevron_up.svg b/src/img/tokens/ic_box_chevron_up.svg new file mode 100644 index 0000000000..2b1f5f4dbf --- /dev/null +++ b/src/img/tokens/ic_box_chevron_up.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/src/locales/de/messages.po b/src/locales/de/messages.po index 0aac6c99c2..1786542653 100644 --- a/src/locales/de/messages.po +++ b/src/locales/de/messages.po @@ -73,6 +73,11 @@ msgstr "PREIS" msgid "receive" msgstr "erhalten" +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx +msgid "Create basic TP/SL orders that fully close your position. For advanced TP/SL setup, use the positions list after opening a position." +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Referrals Dashboard" msgstr "GMX-Referrals-Dashboard" @@ -133,7 +138,7 @@ msgstr "Yield-Optimierer auf Avalanche" msgid "Arbitrum" msgstr "Arbitrum" -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Orders ({0})" msgstr "Orders ({0})" @@ -148,6 +153,8 @@ msgstr "Stop-Loss" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/Referrals/AffiliatesStats.tsx #: src/components/Referrals/TradersStats.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx msgid "Type" @@ -264,6 +271,10 @@ msgstr "Market Swap ausführen" msgid "Discover" msgstr "Entdecken" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Full Position Close" +msgstr "" + #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "There is no price impact for increase orders, orders are filled at the mark price. <0>Read more." msgstr "Für Erhöhungsorders gibt es keinen Preis-Impact; Orders werden zum Mark-Preis ausgeführt. <0>Mehr erfahren." @@ -428,7 +439,6 @@ msgstr "Max. Pool-USD überschritten" #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price below mark price" @@ -641,6 +651,10 @@ msgstr "Telegram-Kontakt (optional)" msgid "Market order will be cancellable in {minutesText}{seconds}s." msgstr "Markt-Order wird in {minutesText}{seconds}s stornierbar sein." +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Edit TP/SL Size" +msgstr "" + #: src/components/UserIncentiveDistribution/AboutGlpIncident.tsx msgid "About GLP Incident" msgstr "Über den GLP-Vorfall" @@ -675,17 +689,19 @@ msgid "Settling" msgstr "Abrechnung" #: src/components/PositionDropdown/PositionDropdown.tsx -#: src/components/PositionItem/PositionItem.tsx msgid "Edit Collateral" msgstr "Kollateral bearbeiten" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "SL Price" +msgstr "" + #: src/components/Earn/Discovery/EarnFaq.tsx msgid "GLV and GM both enable liquidity provision on GMX, allowing users to earn fees from three revenue sources that auto-compound into the token's price. GM tokens represent liquidity in a single pool that supports one specific market on GMX. GLV vaults contain multiple GM tokens in an auto-rebalancing composition, thereby aggregating liquidity across various markets that have the same backing tokens (ex: ETH-USDC, or BTC-USDC). GM enables users to provide isolated liquidity with exposure to a specific market, while GLV allows users to passively earn yield from a diverse range of highly utilized pools." msgstr "GLV und GM ermöglichen beide die Bereitstellung von Liquidität auf GMX und erlauben es Nutzern, Gebühren aus drei Einnahmequellen zu verdienen, die automatisch in den Token-Preis reinvestiert werden. GM-Token repräsentieren Liquidität in einem einzelnen Pool, der einen bestimmten Markt auf GMX unterstützt. GLV-Vaults enthalten mehrere GM-Token in einer automatisch ausbalancierten Zusammensetzung und aggregieren so Liquidität über verschiedene Märkte mit denselben Basis-Token (z. B. ETH-USDC oder BTC-USDC). GM ermöglicht isolierte Liquidität mit Exposure zu einem bestimmten Markt, während GLV passives Yield aus einer Vielzahl hochgenutzter Pools ermöglicht." #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price above mark price" @@ -862,6 +878,10 @@ msgstr "{tokenSymbol} unstaken" msgid "MAX" msgstr "MAX" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Stop Loss Price" +msgstr "" + #: src/pages/BuyGMX/BuyGMX.tsx msgid "No centralized exchanges available for this network." msgstr "Keine zentralisierten Exchanges für dieses Netzwerk verfügbar." @@ -929,6 +949,8 @@ msgstr "Sie navigieren zu GMX Solana, das von einem separaten Team betrieben wir #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx msgid "Pay" msgstr "Bezahlen" @@ -1111,6 +1133,7 @@ msgstr "Erlaube die Übertragung aller meiner Token auf ein neues Konto" #: src/components/OrderItem/OrderItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Collateral" msgstr "Kollateral" @@ -1316,6 +1339,10 @@ msgstr "Min. Order: {0}" msgid "Wallet is not connected" msgstr "Wallet ist nicht verbunden" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of position)" +msgstr "" + #: landing/src/pages/Home/HeroSection/AnimatedTitle.tsx msgid "100+ crypto tokens" msgstr "Über 100 Krypto-Token" @@ -1338,6 +1365,10 @@ msgstr "GM ist der Liquiditätsanbieter-Token für GMX V2-Märkte. Akkumuliert 6 msgid "The current price impact is {0}. Consider adding a buffer of 0.30% to it so the order is more likely to be processed." msgstr "Der aktuelle Preisimpakt beträgt {0}. Erwäge, einen Puffer von 0,30 % hinzuzufügen, damit der Auftrag wahrscheinlicher verarbeitet wird." +#: src/components/TPSLModal/TPSLModal.tsx +msgid "TP/SL: {positionTitle}" +msgstr "" + #: src/components/SettingsModal/RpcDebugSettings.tsx msgid "Public" msgstr "Öffentlich" @@ -1444,6 +1475,10 @@ msgstr "Zeitstempel" msgid "The amount you are trying to withdraw exceeds the limit. Please try an amount smaller than <0>{upperLimitFormatted}." msgstr "Der Betrag, den Sie abheben möchten, überschreitet das Limit. Bitte versuchen Sie einen Betrag kleiner als <0>{upperLimitFormatted}." +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Gain" +msgstr "" + #: src/components/NotifyModal/NotifyModal.tsx msgid "Discover GMX Alerts" msgstr "Entdecke GMX-Alerts" @@ -1668,10 +1703,6 @@ msgstr "Kein Swap-Pfad gefunden" msgid "RPnL" msgstr "RPnL" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined stop losses are at maximum (100%). Decrease existing values to add more orders." -msgstr "Kombinierte Stop-Losses sind am Maximum (100 %). Verringere bestehende Werte, um mehr Orders hinzuzufügen." - #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx msgid "4-18 Places" @@ -1768,7 +1799,6 @@ msgid "Min size per part: {0}" msgstr "Min. Größe pro Teil: {0}" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price above liq. price" msgstr "Trigger-Preis über Liq.-Preis" @@ -1800,7 +1830,6 @@ msgstr "Optionsbasierte Vaults" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionSeller/PositionSeller.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Mark" @@ -1942,6 +1971,7 @@ msgstr "Der USD-Wert der Gebühren wird zum Zeitpunkt ihrer Erhebung berechnet u #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeboxMarginFields/MarginPercentageSlider.tsx msgid "Max" msgstr "Max" @@ -2020,9 +2050,9 @@ msgstr "Kaufe GMX auf {chainName}" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx +#: src/components/TradeboxMarginFields/SizeField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -2174,6 +2204,7 @@ msgstr "Aktuelle Endpunkte ({0})" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Initial collateral (collateral excluding borrow and funding fee)." msgstr "Initiales Collateral (Collateral exklusive Borrow- und Funding-Gebühr)." @@ -2435,10 +2466,6 @@ msgstr "Bridge und Tausch" msgid "The execution price may vary from your set limit price due to fees and price impact, ensuring you receive the displayed minimum receive amount. <0>Read more." msgstr "Der Ausführungspreis kann aufgrund von Gebühren und Preis-Impact von deinem Limit-Preis abweichen, wodurch du den angezeigten Mindestempfangsbetrag erhältst. <0>Mehr erfahren." -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above lowest limit price" -msgstr "Trigger-Preis über dem niedrigsten Limit-Preis" - #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2528,6 +2555,10 @@ msgstr "APRs werden wöchentlich am Mittwoch aktualisiert und hängen von den in msgid "Fees (Incl. Swap)" msgstr "Gebühren (inkl. Tausch)" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "Order update fee" +msgstr "" + #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts msgid "{fromText} to {toMinText}" @@ -2614,6 +2645,7 @@ msgid "Exposure to Backing Tokens" msgstr "Exposition gegenüber Backing-Tokens" #: src/components/Referrals/AddAffiliateCode.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Creating..." msgstr "Wird erstellt…" @@ -2669,7 +2701,9 @@ msgstr "In anderen Tokens:" msgid "Returned Collateral" msgstr "Zurückgegebenes Kollateral" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "There are issues in the TP/SL orders." msgstr "Es gibt Probleme in den TP/SL-Orders." @@ -2762,10 +2796,6 @@ msgstr "Kaufe GMX von zentralisierten Exchanges:" msgid "GMX bonds can be bought on Bond Protocol with a discount and a small vesting period:" msgstr "GMX-Bonds können auf Bond Protocol mit Rabatt und einer kleinen Vesting-Periode gekauft werden:" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above highest limit price" -msgstr "Trigger-Preis über dem höchsten Limit-Preis" - #: src/components/Earn/Discovery/EarnFaq.tsx msgid "Yes, staking and providing liquidity are fully permissionless. The GMX, GLV, and GM tokens are liquid and can be unstaked and withdrawn at any time. For LP tokens, there could be some rare edge cases where your liquidity is being used for active trades. In that case, you can withdraw as soon as positions close." msgstr "Ja, Staking und Liquiditätsbereitstellung sind vollständig permissionless. GMX-, GLV- und GM-Token sind liquide und können jederzeit unstaked und abgezogen werden. Bei LP-Token kann es in seltenen Fällen vorkommen, dass deine Liquidität gerade für offene Positionen genutzt wird – dann kannst du abziehen, sobald die Positionen geschlossen sind." @@ -2895,12 +2925,16 @@ msgstr "<0>Verkaufen von {0}{1}<1>{poolName}" msgid "No eligible tokens available on {0} for deposit" msgstr "Keine berechtigten Tokens auf {0} für Einzahlung verfügbar" +#: src/components/TPSLModal/TPSLInputRow.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PnL" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Max close amount exceeded" msgstr "Max. Schließbetrag überschritten" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2986,6 +3020,7 @@ msgstr "20s" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx @@ -3025,7 +3060,8 @@ msgstr "Unzureichende verfügbare Liquidität. Die Order wird ausgeführt, wenn #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Claims/ClaimsHistory.tsx #: src/components/OrderItem/OrderItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/Referrals/ClaimAffiliatesModal/ClaimAffiliatesModal.tsx #: src/components/TableMarketFilter/MarketFilterBase.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx @@ -3114,7 +3150,6 @@ msgid "Debug values are not available" msgstr "Debug-Werte sind nicht verfügbar" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price below liq. price" msgstr "Trigger-Preis unter Liq.-Preis" @@ -3247,10 +3282,6 @@ msgstr "Durchschn. Größe" msgid "V1 esGMX" msgstr "V1 esGMX" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Take Profit PnL" -msgstr "Take-Profit PnL" - #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your GLV or GM tokens, and borrow against them, or put your LP tokens into strategies to maximize your yield." msgstr "Verleihe deine GLV- oder GM-Token, leihe dagegen oder setze deine LP-Token in Strategien ein, um dein Yield zu maximieren." @@ -3295,6 +3326,7 @@ msgstr "Beanspruchen..." #: src/components/Earn/AdditionalOpportunities/OpportunityFilters.tsx #: src/components/GmxAccountModal/AvailableToTradeAssetsView.tsx #: src/components/TokenSelector/MultichainMarketTokenSelector.tsx +#: src/components/TPSLModal/TPSLModal.tsx msgid "All" msgstr "Alle" @@ -3321,7 +3353,8 @@ msgstr "{longOrShort}-Positionen {fundingAction} eine Finanzierungsgebühr von { #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/domain/synthetics/positions/utils.ts msgid "TWAP" @@ -3401,10 +3434,6 @@ msgstr "Zulässiges Slippage" msgid "{longOrShort} positions {fundingAction} a funding fee of {fundingRate} per hour and do not pay a borrow fee." msgstr "{longOrShort}-Positionen {fundingAction} eine Finanzierungsgebühr von {fundingRate} pro Stunde und zahlen keine Leihgebühr." -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Short TP/SL: {0} a short position when the trigger price is reached." -msgstr "{typeString} Short TP/SL: {0} eine Short-Position, wenn der Trigger-Preis erreicht wird." - #: src/pages/Ecosystem/Ecosystem.tsx msgid "Community-led Telegram groups." msgstr "Von der Community geführte Telegram-Gruppen." @@ -3438,6 +3467,10 @@ msgstr "Markttausch anfordern" msgid "Insufficient liquidity in the {0} market pool. Select a different pool for this market.{1}" msgstr "Unzureichende Liquidität im {0}-Marktpool. Wähle einen anderen Pool für diesen Markt.{1}" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Loss" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "App disabled, pending {0} upgrade" msgstr "App deaktiviert, ausstehendes {0}-Upgrade" @@ -3472,6 +3505,10 @@ msgstr "Erfolgreiches Einlösen von Funding-Gebühren" msgid "<0>every {hours} hours{0}" msgstr "<0>jede {hours} Stunden{0}" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "{secondLabel}" +msgstr "" + #: src/components/DebugExpressSettings/DebugSwapsSettings.tsx #: src/components/DebugSwapsSettings/DebugSwapsSettings.tsx msgid "Fail External Swaps" @@ -3501,13 +3538,17 @@ msgstr "Tausch eingereicht." #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/pages/AccountDashboard/DailyAndCumulativePnL.tsx msgid "PnL" msgstr "PnL" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "No TP/SL orders" +msgstr "" + #: src/components/TradeBox/ExpressTradingWarningCard.tsx msgid "Express Trading is not available using network's native token {0}. Consider using {1} instead." msgstr "Express-Trading ist nicht verfügbar mit dem nativen Token {0} des Netzwerks. Überlege, stattdessen {1} zu verwenden." @@ -3773,7 +3814,6 @@ msgstr "Verdienen" #: src/components/CollateralSelector/CollateralSelector.tsx #: src/components/CollateralSelector/PositionEditorCollateralSelector.tsx -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx msgid "Collateral In" msgstr "Kollateral In" @@ -3785,6 +3825,10 @@ msgstr "GM-Märkte" msgid "Your opinions and experiences matter to us. Your feedback helps us understand what we are doing well and where we can make enhancements." msgstr "Deine Meinungen und Erfahrungen sind uns wichtig. Dein Feedback hilft uns zu verstehen, was wir gut machen und wo wir Verbesserungen vornehmen können." +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Create TP/SL" +msgstr "" + #: src/components/GmxAccountModal/DepositView.tsx #: src/components/GmxAccountModal/WithdrawalView.tsx msgid "Estimated Time" @@ -3823,6 +3867,10 @@ msgstr "Rabatte, die von diesem Konto als Trader verdient wurden." msgid "Transfer account" msgstr "Konto übertragen" +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Cancel all" +msgstr "" + #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your tokens, or borrow against it." @@ -3845,8 +3893,6 @@ msgstr "<0>GMX führt Trades gegen einen dynamisch ausbalancierten Liquiditätsp #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx msgid "Read more" msgstr "Mehr lesen" @@ -3915,6 +3961,7 @@ msgstr "Mehr entdecken" #: src/components/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/AllowedSwapSlippageInputRowImpl/AllowedSwapSlippageInputRowImpl.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/pages/LeaderboardPage/components/LeaderboardAccountsTable.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -3941,6 +3988,8 @@ msgstr "Trigger-Preis für die Order." #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -4009,10 +4058,6 @@ msgstr "Trading-Paar-Preiswarnungen" msgid "Links" msgstr "Links" -#: src/components/PositionItem/PositionItem.tsx -msgid "Use the \"Close\" button to reduce your position via market, TP/SL, or TWAP orders." -msgstr "Verwende die Schaltfläche «Schließen», um deine Position über Market-, TP/SL- oder TWAP-Orders zu reduzieren." - #: src/components/Seo/SEO.tsx msgid "GMX | Decentralized Perpetual Exchange" msgstr "GMX | Dezentrale Perpetual-Exchange" @@ -4227,7 +4272,7 @@ msgstr "Jobs" msgid "Decentralized Options Strategies" msgstr "Dezentrale Optionsstrategien" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This action will not apply for that position. <0>Switch to {1} collateral" msgstr "Sie haben eine bestehende Position mit {0} als Collateral. Diese Aktion gilt nicht für diese Position. <0>Wechseln zu {1} Collateral" @@ -4267,7 +4312,9 @@ msgid "Amount of traders you referred." msgstr "Anzahl der von dir geworbenen Trader." #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Take Profit" msgstr "Take-Profit" @@ -4285,6 +4332,14 @@ msgstr "{typeString} Long Stop Market: {0} eine Long-Position, wenn der Preis un msgid "Insufficient {0} liquidity" msgstr "Unzureichende {0}-Liquidität" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Unable to calculate order" +msgstr "" + +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "TP/SL: {positionTitle} Decrease" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.tsx #: src/pages/Ecosystem/Ecosystem.tsx msgid "Creator" @@ -4310,6 +4365,10 @@ msgstr "Limit erstellen" msgid "Transfer already initiated" msgstr "Übertragung bereits gestartet" +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +msgid "Margin to Pay" +msgstr "" + #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx msgid "Withdraw submitted." msgstr "Abheben eingereicht." @@ -4387,7 +4446,8 @@ msgstr "Botanix" msgid "Accrued price impact rebates. They will become claimable after 5 days.<0/><1/><2>Read more." msgstr "Angefallene Preisauswirkungs-Rabatte. Sie werden nach 5 Tagen einlösbar.<0/><1/><2>Mehr lesen." -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "Take Profit / Stop Loss" msgstr "Take-Profit / Stop-Loss" @@ -4483,6 +4543,10 @@ msgstr "Keine Märkte gefunden." msgid "RPC Monitoring" msgstr "RPC-Überwachung" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the \"Close\" button to reduce your position via market or TWAP orders." +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "ODOS" msgstr "ODOS" @@ -4544,10 +4608,6 @@ msgstr "Hoher Preis-Impact" msgid "<0>Solana Support<1>Botanix Support<2>GMX Express" msgstr "<0>Solana-Unterstützung<1>Botanix-Unterstützung<2>GMX Express" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined take profits are at maximum (100%). Decrease existing values to add more orders." -msgstr "Kombinierte Take-Profits sind am Maximum (100%). Verringere bestehende Werte, um mehr Orders hinzuzufügen." - #: src/components/GmList/GmList.tsx msgid "POOL" msgstr "POOL" @@ -4580,6 +4640,8 @@ msgstr "Verkaufen" msgid "Recommended" msgstr "Empfohlen" +#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Decrease" @@ -4783,11 +4845,12 @@ msgstr "V1 Avalanche" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Collateral ({0})" msgstr "Kollateral ({0})" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This order will not be valid for that position. <0>Switch to {1} collateral" msgstr "Du hast eine bestehende Position mit {0} als Collateral. Diese Order ist für diese Position nicht gültig. <0>Wechsle zu {1}-Collateral" @@ -4890,10 +4953,6 @@ msgstr "Order-Fehler. Preise sind derzeit volatil für diesen Markt, versuche es msgid "Swap Price Impact for External Swap Threshold" msgstr "Swap-Preisimpact für externen Swap-Schwellenwert" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Limit / Take Profit / Stop Loss" -msgstr "Limit / Take Profit / Stop Loss" - #: landing/src/pages/Home/LiqiuditySection/PoolCards.tsx msgid "Steady returns without management" msgstr "Stabile Renditen ohne Management" @@ -4906,10 +4965,6 @@ msgstr "Spread" msgid "This position was liquidated as the max. leverage of {formattedMaxLeverage} was exceeded when taking into account fees." msgstr "Diese Position wurde liquidiert, da die maximale Hebelwirkung von {formattedMaxLeverage} überschritten wurde, wenn die Gebühren berücksichtigt werden." -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Swap {0}" -msgstr "Swap {0}" - #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx @@ -4999,10 +5054,6 @@ msgstr "wstETH APR" msgid "Telegram Group" msgstr "Telegram Gruppe" -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Long TP/SL: {0} a long position when the trigger price is reached." -msgstr "{typeString} Long TP/SL: {0} eine Long-Position, wenn der Trigger-Preis erreicht wird." - #: src/components/OrderItem/TwapOrdersList/TwapOrdersList.tsx msgid "<0>{fromTokenText} {fromTokenIcon}<1> to {toTokenIcon}" msgstr "<0>{fromTokenText} {fromTokenIcon}<1> zu {toTokenIcon}" @@ -5031,7 +5082,7 @@ msgstr "" #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/GmxAssetCard.tsx #: src/components/MarketStats/MarketGraphs.tsx #: src/components/SwapCard/SwapCard.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TradeboxMarginFields/PriceField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TVChart/Chart.tsx #: src/pages/Dashboard/GmxCard.tsx @@ -5194,6 +5245,10 @@ msgstr "Bridge-TX-Hash" msgid "Start unrealized pnl" msgstr "Start unrealisierter PnL" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of Position)" +msgstr "" + #: src/domain/tokens/approveTokens.tsx msgid "Approval submitted! <0>View status." msgstr "Genehmigung übermittelt! <0>Status anzeigen." @@ -5215,7 +5270,7 @@ msgid "Stake GMX to earn GMX rewards" msgstr "Stake GMX, um GMX-Belohnungen zu verdienen" #: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionList/PositionList.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "TP/SL" msgstr "TP/SL" @@ -5443,6 +5498,7 @@ msgstr "Governance" #: src/components/OrderItem/OrderItem.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Cancel" msgstr "Abbrechen" @@ -5509,6 +5565,11 @@ msgstr "90T" msgid "Liquidation" msgstr "Liquidation" +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Add TP/SL" +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Risk Monitoring" msgstr "GMX Risikoüberwachung" @@ -5553,6 +5614,7 @@ msgid "Longs Net Rate / 1h" msgstr "Longs Netto-Rate / 1h" #: src/components/OrderItem/OrderItem.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx msgid "Edit" msgstr "Bearbeiten" @@ -5565,10 +5627,6 @@ msgstr "Keine verfügbare Hebelwirkung gefunden" msgid "You have a <0>pending transfer to {pendingReceiver}." msgstr "Du hast eine <0>ausstehende Übertragung an {pendingReceiver}." -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Limit size is required" -msgstr "Limit-Größe ist erforderlich" - #: src/components/GmxAccountModal/DepositView.tsx msgid "From Network" msgstr "Vom Netzwerk" @@ -5585,6 +5643,10 @@ msgstr "Vom GMX-Konto abheben" msgid "Collateral at Liquidation" msgstr "Kollateral bei Liquidation" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Keep leverage at {currentLeverage}" +msgstr "" + #: src/lib/dates.ts #: src/pages/AccountDashboard/GeneralPerformanceDetails.tsx msgid "Today" @@ -5969,10 +6031,6 @@ msgstr "Negative Funding-Gebühr" msgid "{fromText} to {toExecutionText}" msgstr "{fromText} zu {toExecutionText}" -#: src/components/PositionSeller/PositionSeller.tsx -msgid "Create {0} Order" -msgstr "{0}-Order erstellen" - #: src/context/SubaccountContext/SubaccountContextProvider.tsx msgid "Settings updated." msgstr "Einstellungen aktualisiert." @@ -6050,11 +6108,6 @@ msgstr "Swap {fromAmount} für {toAmount}" msgid "Amount" msgstr "Menge" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create {0} order" -msgstr "{0} Order erstellen" - #: src/components/PositionEditor/PositionEditor.tsx msgid "Edit {0} {1}{2}" msgstr "{0} {1}{2} bearbeiten" @@ -6136,6 +6189,7 @@ msgstr "Es könnte nicht ausreichend Liquidität geben, um Ihre Order auszuführ #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -6242,6 +6296,7 @@ msgstr "Verkaufsgebühr" #: src/components/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Execution Details" msgstr "Ausführungsdetails" @@ -6266,7 +6321,7 @@ msgstr "Take Profit aktualisieren" msgid "Only addresses with over {0} in capital used are ranked.<0/><1/>The capital used is calculated as the highest value of [<2>sum of collateral of open positions - realized PnL + period start pending PnL]." msgstr "Nur Adressen mit über {0} eingesetztem Kapital werden gerankt.<0/><1/>Das eingesetzte Kapital wird als höchster Wert von [<2>Summe des Collaterals offener Positionen - realisiertes PnL + anfängliches ausstehendes PnL] berechnet." -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Active Orders" msgstr "Aktive Orders" @@ -6317,6 +6372,10 @@ msgstr "Protokoll-Analytik" msgid "More active management required compared to GLV" msgstr "Mehr aktives Management erforderlich im Vergleich zu GLV" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Take Profit Price" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed amount" msgstr "Gebühren überschreiten Betrag" @@ -6419,6 +6478,10 @@ msgstr "Kudai AI Agent" msgid "Current Price" msgstr "Aktueller Preis" +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +msgid "Enter TP/SL price" +msgstr "" + #: src/pages/NftWallet/NftWallet.jsx msgid "Enter NFT ID" msgstr "NFT ID eingeben" @@ -6465,10 +6528,6 @@ msgstr "Einzahlung" msgid "365d Est. Fees" msgstr "365T Geschätzte Gebühren" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below highest limit price" -msgstr "Trigger-Preis unter höchstem Limit-Preis" - #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/ClaimModal.tsx msgid "Select rewards to claim" msgstr "Zu beanspruchende Belohnungen auswählen" @@ -6536,7 +6595,9 @@ msgstr "<0>{0} beanspruchen" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx @@ -6581,7 +6642,9 @@ msgid "We Value Your Feedback" msgstr "Wir schätzen dein Feedback" #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Stop Loss" msgstr "Stop Loss" @@ -6634,6 +6697,10 @@ msgstr "Fehler beim Übermitteln der Order" msgid "Duration" msgstr "Dauer" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "TP Price" +msgstr "" + #: src/components/Seo/SEO.tsx msgid "Trade spot or perpetual BTC, ETH, AVAX and other top cryptocurrencies with up to 100x leverage directly from your wallet on Arbitrum and Avalanche." msgstr "Trade Spot oder Perpetual BTC, ETH, AVAX und andere Top-Kryptowährungen mit bis zu 100x Hebelwirkung direkt aus deinem Wallet auf Arbitrum und Avalanche." @@ -6790,7 +6857,8 @@ msgstr "GMX-Belohnungen:" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderItem/OrderItem.tsx #: src/components/OrderList/OrderList.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx msgid "Trigger Price" @@ -7025,6 +7093,10 @@ msgstr "Verschieben" msgid "Why is the claim in GLV tokens?" msgstr "Warum erfolgt der Anspruch in GLV-Tokens?" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PNL" +msgstr "" + #: src/components/SettingsModal/TradingSettings.tsx msgid "Express + One-Click" msgstr "Express + One-Click" @@ -7091,6 +7163,8 @@ msgstr "Verschiebungsfehler." #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -7109,7 +7183,7 @@ msgstr "Long" #: src/components/PoolSelector2/PoolSelector2.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx -#: src/components/TradeBox/MarketPoolSelectorRow.tsx +#: src/components/TradeBox/MarketPoolSelectorField.tsx msgid "Pool" msgstr "Pool" @@ -7202,6 +7276,8 @@ msgstr "SNAPSHOT" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Liquidation Price" msgstr "Liquidationspreis" @@ -7259,6 +7335,7 @@ msgstr "Bestätigen..." msgid "Deposited:" msgstr "Eingezahlt:" +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Increase" @@ -7384,6 +7461,10 @@ msgstr "Abgeschlossen" msgid "Referral Terms" msgstr "Referral-Bedingungen" +#: src/components/PositionDropdown/PositionDropdown.tsx +msgid "Increase Size (TWAP)" +msgstr "" + #: src/components/Earn/Portfolio/RecommendedAssets/RecommendedAssets.tsx #: src/components/GmList/FeeApyLabel.tsx msgid "Fee APY" @@ -7394,7 +7475,6 @@ msgid "Can I build on top of GMX or integrate it into my DeFi app?" msgstr "Kann ich auf GMX aufbauen oder es in meine DeFi-App integrieren?" #: src/components/GmSwap/GmSwapBox/GmSwapWarningsRow.tsx -#: src/components/TradeBox/TradeBox.tsx msgid "Support for GMX accounts on Avalanche will be discontinued soon. Opening new positions from and depositing additional funds to Avalanche GMX accounts is no longer available. We recommend switching to Arbitrum as a settlement network." msgstr "Die Unterstützung für GMX-Konten auf Avalanche wird bald eingestellt. Das Eröffnen neuer Positionen von und die Einzahlung zusätzlicher Mittel auf Avalanche-GMX-Konten ist nicht mehr verfügbar. Wir empfehlen, zu Arbitrum als Abrechnungsnetzwerk zu wechseln." @@ -7437,6 +7517,10 @@ msgstr "Nur Buchstaben, Zahlen und Unterstriche erlaubt." msgid "High TWAP network fee" msgstr "Hohe TWAP-Netzwerkgebühr" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the TP/SL button to set TP/SL orders." +msgstr "" + #: src/components/Earn/Discovery/EarnProductCard.tsx msgid "What is GM?" msgstr "Was ist GM?" @@ -7562,6 +7646,7 @@ msgstr "<0>jede {minutes} Minuten{0}" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/EntryPriceRow.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Entry Price" @@ -7636,10 +7721,6 @@ msgstr "Wandle esGMX-Token in GMX-Token um. Bitte lies die Vesting-Details, bevo msgid "You have an existing position in the {0} market pool.<0>Switch to {1} market pool" msgstr "Sie haben eine bestehende Position im {0}-Marktpool.<0>Wechseln zum {1}-Marktpool" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Stop Loss PnL" -msgstr "Stop-Loss-Gewinn/Verlust" - #: src/components/TVChartContainer/constants.ts msgid "Market - Long Dec." msgstr "Markt - Long Abn." @@ -7787,6 +7868,7 @@ msgstr "Mittel einlösen" msgid "30d Fee APY" msgstr "30-Tage-Gebühren-APY" +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "Net Price Impact / Fees" msgstr "Netto-Preisauswirkung / Gebühren" @@ -7858,6 +7940,7 @@ msgstr "Referral-Code generieren" #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/SwapCard/SwapCard.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "Swap" msgstr "Tauschen" @@ -7896,10 +7979,6 @@ msgstr "Sprache" msgid "Complete Transfer" msgstr "Übertragung abschließen" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below lowest limit price" -msgstr "Triggerpreis unter niedrigstem Limitpreis" - #: src/components/Referrals/TradersStats.tsx msgid "GMX V2 discounts are automatically applied on each trade and are not displayed on this table." msgstr "GMX V2-Rabatte werden automatisch auf jeden Handel angewendet und nicht in dieser Tabelle angezeigt." @@ -7976,10 +8055,6 @@ msgstr "Letzte 30T" msgid "Dashboards" msgstr "Dashboards" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create TWAP {0} order" -msgstr "TWAP {0}-Order erstellen" - #: src/components/AddressDropdown/AddressDropdownWithoutMultichain.tsx msgid "Address copied to your clipboard." msgstr "Adresse in Ihre Zwischenablage kopiert." @@ -8006,6 +8081,10 @@ msgstr "App öffnen" msgid "7d" msgstr "7T" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "TP/SL fees" +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "Rage Trade" msgstr "Rage Trade" @@ -8032,6 +8111,7 @@ msgstr "Übertragung fehlgeschlagen" #: src/components/OrderList/OrderList.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Mark Price" @@ -8153,7 +8233,7 @@ msgstr "Sehr wahrscheinlich" msgid "Withdrawing funds from GMX Account..." msgstr "Mittel werden vom GMX-Konto abgehoben…" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing limit order with {symbol} as collateral. <0>Switch to {symbol} collateral" msgstr "Sie haben eine bestehende Limit-Order mit {symbol} als Collateral. <0>Wechseln zu {symbol} Collateral" @@ -8272,7 +8352,6 @@ msgstr "Laden" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/TradeBox/tradeboxConstants.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx #: src/components/TradeHistory/keys.ts #: src/domain/synthetics/positions/utils.ts msgid "Limit" @@ -8324,10 +8403,9 @@ msgid "multiple asset classes" msgstr "mehrere Asset-Klassen" #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/multichain/progress/MultichainTransferProgressView.tsx diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index 13b8034279..8f6956335a 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -73,6 +73,11 @@ msgstr "PRICE" msgid "receive" msgstr "receive" +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx +msgid "Create basic TP/SL orders that fully close your position. For advanced TP/SL setup, use the positions list after opening a position." +msgstr "Create basic TP/SL orders that fully close your position. For advanced TP/SL setup, use the positions list after opening a position." + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Referrals Dashboard" msgstr "GMX Referrals Dashboard" @@ -133,7 +138,7 @@ msgstr "Yield Optimizer on Avalanche" msgid "Arbitrum" msgstr "Arbitrum" -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Orders ({0})" msgstr "Orders ({0})" @@ -148,6 +153,8 @@ msgstr "stop loss" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/Referrals/AffiliatesStats.tsx #: src/components/Referrals/TradersStats.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx msgid "Type" @@ -264,6 +271,10 @@ msgstr "Execute Market Swap" msgid "Discover" msgstr "Discover" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Full Position Close" +msgstr "Full Position Close" + #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "There is no price impact for increase orders, orders are filled at the mark price. <0>Read more." msgstr "There is no price impact for increase orders, orders are filled at the mark price. <0>Read more." @@ -428,7 +439,6 @@ msgstr "Max pool USD exceeded" #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price below mark price" @@ -641,6 +651,10 @@ msgstr "Telegram contact (optional)" msgid "Market order will be cancellable in {minutesText}{seconds}s." msgstr "Market order will be cancellable in {minutesText}{seconds}s." +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Edit TP/SL Size" +msgstr "Edit TP/SL Size" + #: src/components/UserIncentiveDistribution/AboutGlpIncident.tsx msgid "About GLP Incident" msgstr "About GLP Incident" @@ -675,17 +689,19 @@ msgid "Settling" msgstr "Settling" #: src/components/PositionDropdown/PositionDropdown.tsx -#: src/components/PositionItem/PositionItem.tsx msgid "Edit Collateral" msgstr "Edit Collateral" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "SL Price" +msgstr "SL Price" + #: src/components/Earn/Discovery/EarnFaq.tsx msgid "GLV and GM both enable liquidity provision on GMX, allowing users to earn fees from three revenue sources that auto-compound into the token's price. GM tokens represent liquidity in a single pool that supports one specific market on GMX. GLV vaults contain multiple GM tokens in an auto-rebalancing composition, thereby aggregating liquidity across various markets that have the same backing tokens (ex: ETH-USDC, or BTC-USDC). GM enables users to provide isolated liquidity with exposure to a specific market, while GLV allows users to passively earn yield from a diverse range of highly utilized pools." msgstr "GLV and GM both enable liquidity provision on GMX, allowing users to earn fees from three revenue sources that auto-compound into the token's price. GM tokens represent liquidity in a single pool that supports one specific market on GMX. GLV vaults contain multiple GM tokens in an auto-rebalancing composition, thereby aggregating liquidity across various markets that have the same backing tokens (ex: ETH-USDC, or BTC-USDC). GM enables users to provide isolated liquidity with exposure to a specific market, while GLV allows users to passively earn yield from a diverse range of highly utilized pools." #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price above mark price" @@ -862,6 +878,10 @@ msgstr "Unstake {tokenSymbol}" msgid "MAX" msgstr "MAX" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Stop Loss Price" +msgstr "Stop Loss Price" + #: src/pages/BuyGMX/BuyGMX.tsx msgid "No centralized exchanges available for this network." msgstr "No centralized exchanges available for this network." @@ -929,6 +949,8 @@ msgstr "You're about to navigate to GMX Solana, which is operated by a separate #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx msgid "Pay" msgstr "Pay" @@ -1111,6 +1133,7 @@ msgstr "Allow all my tokens to be transferred to a new account" #: src/components/OrderItem/OrderItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Collateral" msgstr "Collateral" @@ -1316,6 +1339,10 @@ msgstr "Min order: {0}" msgid "Wallet is not connected" msgstr "Wallet is not connected" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of position)" +msgstr "Size (% of position)" + #: landing/src/pages/Home/HeroSection/AnimatedTitle.tsx msgid "100+ crypto tokens" msgstr "100+ crypto tokens" @@ -1338,6 +1365,10 @@ msgstr "GM is the liquidity provider token for GMX V2 markets. Accrues 63% of th msgid "The current price impact is {0}. Consider adding a buffer of 0.30% to it so the order is more likely to be processed." msgstr "The current price impact is {0}. Consider adding a buffer of 0.30% to it so the order is more likely to be processed." +#: src/components/TPSLModal/TPSLModal.tsx +msgid "TP/SL: {positionTitle}" +msgstr "TP/SL: {positionTitle}" + #: src/components/SettingsModal/RpcDebugSettings.tsx msgid "Public" msgstr "Public" @@ -1444,6 +1475,10 @@ msgstr "Timestamp" msgid "The amount you are trying to withdraw exceeds the limit. Please try an amount smaller than <0>{upperLimitFormatted}." msgstr "The amount you are trying to withdraw exceeds the limit. Please try an amount smaller than <0>{upperLimitFormatted}." +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Gain" +msgstr "Gain" + #: src/components/NotifyModal/NotifyModal.tsx msgid "Discover GMX Alerts" msgstr "Discover GMX Alerts" @@ -1668,10 +1703,6 @@ msgstr "No swap path found" msgid "RPnL" msgstr "RPnL" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined stop losses are at maximum (100%). Decrease existing values to add more orders." -msgstr "Combined stop losses are at maximum (100%). Decrease existing values to add more orders." - #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx msgid "4-18 Places" @@ -1768,7 +1799,6 @@ msgid "Min size per part: {0}" msgstr "Min size per part: {0}" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price above liq. price" msgstr "Trigger price above liq. price" @@ -1800,7 +1830,6 @@ msgstr "Option-based Vaults" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionSeller/PositionSeller.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Mark" @@ -1942,6 +1971,7 @@ msgstr "The fees' USD value is calculated at the time they are earned and does n #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeboxMarginFields/MarginPercentageSlider.tsx msgid "Max" msgstr "Max" @@ -2020,9 +2050,9 @@ msgstr "Buy GMX on {chainName}" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx +#: src/components/TradeboxMarginFields/SizeField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -2174,6 +2204,7 @@ msgstr "Current Endpoints ({0})" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Initial collateral (collateral excluding borrow and funding fee)." msgstr "Initial collateral (collateral excluding borrow and funding fee)." @@ -2435,10 +2466,6 @@ msgstr "Bridge and swap" msgid "The execution price may vary from your set limit price due to fees and price impact, ensuring you receive the displayed minimum receive amount. <0>Read more." msgstr "The execution price may vary from your set limit price due to fees and price impact, ensuring you receive the displayed minimum receive amount. <0>Read more." -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above lowest limit price" -msgstr "Trigger price above lowest limit price" - #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2528,6 +2555,10 @@ msgstr "APRs are updated weekly on Wednesday and will depend on the fees collect msgid "Fees (Incl. Swap)" msgstr "Fees (Incl. Swap)" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "Order update fee" +msgstr "Order update fee" + #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts msgid "{fromText} to {toMinText}" @@ -2614,6 +2645,7 @@ msgid "Exposure to Backing Tokens" msgstr "Exposure to Backing Tokens" #: src/components/Referrals/AddAffiliateCode.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Creating..." msgstr "Creating..." @@ -2669,7 +2701,9 @@ msgstr "In other tokens:" msgid "Returned Collateral" msgstr "Returned Collateral" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "There are issues in the TP/SL orders." msgstr "There are issues in the TP/SL orders." @@ -2762,10 +2796,6 @@ msgstr "Buy GMX from centralized exchanges:" msgid "GMX bonds can be bought on Bond Protocol with a discount and a small vesting period:" msgstr "GMX bonds can be bought on Bond Protocol with a discount and a small vesting period:" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above highest limit price" -msgstr "Trigger price above highest limit price" - #: src/components/Earn/Discovery/EarnFaq.tsx msgid "Yes, staking and providing liquidity are fully permissionless. The GMX, GLV, and GM tokens are liquid and can be unstaked and withdrawn at any time. For LP tokens, there could be some rare edge cases where your liquidity is being used for active trades. In that case, you can withdraw as soon as positions close." msgstr "Yes, staking and providing liquidity are fully permissionless. The GMX, GLV, and GM tokens are liquid and can be unstaked and withdrawn at any time. For LP tokens, there could be some rare edge cases where your liquidity is being used for active trades. In that case, you can withdraw as soon as positions close." @@ -2895,12 +2925,16 @@ msgstr "<0>Selling {0}{1}<1>{poolName}" msgid "No eligible tokens available on {0} for deposit" msgstr "No eligible tokens available on {0} for deposit" +#: src/components/TPSLModal/TPSLInputRow.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PnL" +msgstr "Est. PnL" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Max close amount exceeded" msgstr "Max close amount exceeded" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2986,6 +3020,7 @@ msgstr "20s" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx @@ -3025,7 +3060,8 @@ msgstr "Insufficient available liquidity. The order will fill when there is liqu #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Claims/ClaimsHistory.tsx #: src/components/OrderItem/OrderItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/Referrals/ClaimAffiliatesModal/ClaimAffiliatesModal.tsx #: src/components/TableMarketFilter/MarketFilterBase.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx @@ -3114,7 +3150,6 @@ msgid "Debug values are not available" msgstr "Debug values are not available" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price below liq. price" msgstr "Trigger price below liq. price" @@ -3247,10 +3282,6 @@ msgstr "Avg. Size" msgid "V1 esGMX" msgstr "V1 esGMX" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Take Profit PnL" -msgstr "Take Profit PnL" - #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your GLV or GM tokens, and borrow against them, or put your LP tokens into strategies to maximize your yield." msgstr "Lend out your GLV or GM tokens, and borrow against them, or put your LP tokens into strategies to maximize your yield." @@ -3295,6 +3326,7 @@ msgstr "Claiming..." #: src/components/Earn/AdditionalOpportunities/OpportunityFilters.tsx #: src/components/GmxAccountModal/AvailableToTradeAssetsView.tsx #: src/components/TokenSelector/MultichainMarketTokenSelector.tsx +#: src/components/TPSLModal/TPSLModal.tsx msgid "All" msgstr "All" @@ -3321,7 +3353,8 @@ msgstr "{longOrShort} positions {fundingAction} a funding fee of {fundingRate} p #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/domain/synthetics/positions/utils.ts msgid "TWAP" @@ -3401,10 +3434,6 @@ msgstr "Allowed Slippage" msgid "{longOrShort} positions {fundingAction} a funding fee of {fundingRate} per hour and do not pay a borrow fee." msgstr "{longOrShort} positions {fundingAction} a funding fee of {fundingRate} per hour and do not pay a borrow fee." -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Short TP/SL: {0} a short position when the trigger price is reached." -msgstr "{typeString} Short TP/SL: {0} a short position when the trigger price is reached." - #: src/pages/Ecosystem/Ecosystem.tsx msgid "Community-led Telegram groups." msgstr "Community-led Telegram groups." @@ -3438,6 +3467,10 @@ msgstr "Request Market Swap" msgid "Insufficient liquidity in the {0} market pool. Select a different pool for this market.{1}" msgstr "Insufficient liquidity in the {0} market pool. Select a different pool for this market.{1}" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Loss" +msgstr "Loss" + #: src/domain/synthetics/trade/utils/validation.ts msgid "App disabled, pending {0} upgrade" msgstr "App disabled, pending {0} upgrade" @@ -3472,6 +3505,10 @@ msgstr "Success claiming funding fees" msgid "<0>every {hours} hours{0}" msgstr "<0>every {hours} hours{0}" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "{secondLabel}" +msgstr "{secondLabel}" + #: src/components/DebugExpressSettings/DebugSwapsSettings.tsx #: src/components/DebugSwapsSettings/DebugSwapsSettings.tsx msgid "Fail External Swaps" @@ -3501,13 +3538,17 @@ msgstr "Swap submitted." #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/pages/AccountDashboard/DailyAndCumulativePnL.tsx msgid "PnL" msgstr "PnL" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "No TP/SL orders" +msgstr "No TP/SL orders" + #: src/components/TradeBox/ExpressTradingWarningCard.tsx msgid "Express Trading is not available using network's native token {0}. Consider using {1} instead." msgstr "Express Trading is not available using network's native token {0}. Consider using {1} instead." @@ -3773,7 +3814,6 @@ msgstr "Earn" #: src/components/CollateralSelector/CollateralSelector.tsx #: src/components/CollateralSelector/PositionEditorCollateralSelector.tsx -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx msgid "Collateral In" msgstr "Collateral In" @@ -3785,6 +3825,10 @@ msgstr "GM Markets" msgid "Your opinions and experiences matter to us. Your feedback helps us understand what we are doing well and where we can make enhancements." msgstr "Your opinions and experiences matter to us. Your feedback helps us understand what we are doing well and where we can make enhancements." +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Create TP/SL" +msgstr "Create TP/SL" + #: src/components/GmxAccountModal/DepositView.tsx #: src/components/GmxAccountModal/WithdrawalView.tsx msgid "Estimated Time" @@ -3823,6 +3867,10 @@ msgstr "Rebates earned by this account as a trader." msgid "Transfer account" msgstr "Transfer account" +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Cancel all" +msgstr "Cancel all" + #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your tokens, or borrow against it." @@ -3845,8 +3893,6 @@ msgstr "<0>GMX executes trades against a dynamically balanced liquidity pool, un #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx msgid "Read more" msgstr "Read more" @@ -3915,6 +3961,7 @@ msgstr "Explore more" #: src/components/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/AllowedSwapSlippageInputRowImpl/AllowedSwapSlippageInputRowImpl.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/pages/LeaderboardPage/components/LeaderboardAccountsTable.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -3941,6 +3988,8 @@ msgstr "Trigger price for the order." #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -4009,10 +4058,6 @@ msgstr "Trading Pair Price Alerts" msgid "Links" msgstr "Links" -#: src/components/PositionItem/PositionItem.tsx -msgid "Use the \"Close\" button to reduce your position via market, TP/SL, or TWAP orders." -msgstr "Use the \"Close\" button to reduce your position via market, TP/SL, or TWAP orders." - #: src/components/Seo/SEO.tsx msgid "GMX | Decentralized Perpetual Exchange" msgstr "GMX | Decentralized Perpetual Exchange" @@ -4227,7 +4272,7 @@ msgstr "Jobs" msgid "Decentralized Options Strategies" msgstr "Decentralized Options Strategies" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This action will not apply for that position. <0>Switch to {1} collateral" msgstr "You have an existing position with {0} as collateral. This action will not apply for that position. <0>Switch to {1} collateral" @@ -4267,7 +4312,9 @@ msgid "Amount of traders you referred." msgstr "Amount of traders you referred." #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Take Profit" msgstr "Take Profit" @@ -4285,6 +4332,14 @@ msgstr "{typeString} Long Stop Market: {0} a long position when the price is bel msgid "Insufficient {0} liquidity" msgstr "Insufficient {0} liquidity" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Unable to calculate order" +msgstr "Unable to calculate order" + +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "TP/SL: {positionTitle} Decrease" +msgstr "TP/SL: {positionTitle} Decrease" + #: src/pages/Ecosystem/Ecosystem.tsx #: src/pages/Ecosystem/Ecosystem.tsx msgid "Creator" @@ -4310,6 +4365,10 @@ msgstr "Create Limit" msgid "Transfer already initiated" msgstr "Transfer already initiated" +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +msgid "Margin to Pay" +msgstr "Margin to Pay" + #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx msgid "Withdraw submitted." msgstr "Withdraw submitted." @@ -4387,7 +4446,8 @@ msgstr "Botanix" msgid "Accrued price impact rebates. They will become claimable after 5 days.<0/><1/><2>Read more." msgstr "Accrued price impact rebates. They will become claimable after 5 days.<0/><1/><2>Read more." -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "Take Profit / Stop Loss" msgstr "Take Profit / Stop Loss" @@ -4483,6 +4543,10 @@ msgstr "No markets found." msgid "RPC Monitoring" msgstr "RPC Monitoring" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the \"Close\" button to reduce your position via market or TWAP orders." +msgstr "Use the \"Close\" button to reduce your position via market or TWAP orders." + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "ODOS" msgstr "ODOS" @@ -4544,10 +4608,6 @@ msgstr "High price impact" msgid "<0>Solana Support<1>Botanix Support<2>GMX Express" msgstr "<0>Solana Support<1>Botanix Support<2>GMX Express" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined take profits are at maximum (100%). Decrease existing values to add more orders." -msgstr "Combined take profits are at maximum (100%). Decrease existing values to add more orders." - #: src/components/GmList/GmList.tsx msgid "POOL" msgstr "POOL" @@ -4580,6 +4640,8 @@ msgstr "Selling" msgid "Recommended" msgstr "Recommended" +#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Decrease" @@ -4783,11 +4845,12 @@ msgstr "V1 Avalanche" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Collateral ({0})" msgstr "Collateral ({0})" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This order will not be valid for that position. <0>Switch to {1} collateral" msgstr "You have an existing position with {0} as collateral. This order will not be valid for that position. <0>Switch to {1} collateral" @@ -4890,10 +4953,6 @@ msgstr "Order error. Prices are currently volatile for this market, try again by msgid "Swap Price Impact for External Swap Threshold" msgstr "Swap Price Impact for External Swap Threshold" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Limit / Take Profit / Stop Loss" -msgstr "Limit / Take Profit / Stop Loss" - #: landing/src/pages/Home/LiqiuditySection/PoolCards.tsx msgid "Steady returns without management" msgstr "Steady returns without management" @@ -4906,10 +4965,6 @@ msgstr "Spread" msgid "This position was liquidated as the max. leverage of {formattedMaxLeverage} was exceeded when taking into account fees." msgstr "This position was liquidated as the max. leverage of {formattedMaxLeverage} was exceeded when taking into account fees." -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Swap {0}" -msgstr "Swap {0}" - #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx @@ -4999,10 +5054,6 @@ msgstr "wstETH APR" msgid "Telegram Group" msgstr "Telegram Group" -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Long TP/SL: {0} a long position when the trigger price is reached." -msgstr "{typeString} Long TP/SL: {0} a long position when the trigger price is reached." - #: src/components/OrderItem/TwapOrdersList/TwapOrdersList.tsx msgid "<0>{fromTokenText} {fromTokenIcon}<1> to {toTokenIcon}" msgstr "<0>{fromTokenText} {fromTokenIcon}<1> to {toTokenIcon}" @@ -5031,7 +5082,7 @@ msgstr "Trade directly from wallet or use GMX Account." #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/GmxAssetCard.tsx #: src/components/MarketStats/MarketGraphs.tsx #: src/components/SwapCard/SwapCard.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TradeboxMarginFields/PriceField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TVChart/Chart.tsx #: src/pages/Dashboard/GmxCard.tsx @@ -5194,6 +5245,10 @@ msgstr "Bridge TX hash" msgid "Start unrealized pnl" msgstr "Start unrealized pnl" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of Position)" +msgstr "Size (% of Position)" + #: src/domain/tokens/approveTokens.tsx msgid "Approval submitted! <0>View status." msgstr "Approval submitted! <0>View status." @@ -5215,7 +5270,7 @@ msgid "Stake GMX to earn GMX rewards" msgstr "Stake GMX to earn GMX rewards" #: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionList/PositionList.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "TP/SL" msgstr "TP/SL" @@ -5443,6 +5498,7 @@ msgstr "Governance" #: src/components/OrderItem/OrderItem.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Cancel" msgstr "Cancel" @@ -5509,6 +5565,11 @@ msgstr "90d" msgid "Liquidation" msgstr "Liquidation" +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Add TP/SL" +msgstr "Add TP/SL" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Risk Monitoring" msgstr "GMX Risk Monitoring" @@ -5553,6 +5614,7 @@ msgid "Longs Net Rate / 1h" msgstr "Longs Net Rate / 1h" #: src/components/OrderItem/OrderItem.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx msgid "Edit" msgstr "Edit" @@ -5565,10 +5627,6 @@ msgstr "No available leverage found" msgid "You have a <0>pending transfer to {pendingReceiver}." msgstr "You have a <0>pending transfer to {pendingReceiver}." -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Limit size is required" -msgstr "Limit size is required" - #: src/components/GmxAccountModal/DepositView.tsx msgid "From Network" msgstr "From Network" @@ -5585,6 +5643,10 @@ msgstr "Withdraw from GMX Account" msgid "Collateral at Liquidation" msgstr "Collateral at Liquidation" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Keep leverage at {currentLeverage}" +msgstr "Keep leverage at {currentLeverage}" + #: src/lib/dates.ts #: src/pages/AccountDashboard/GeneralPerformanceDetails.tsx msgid "Today" @@ -5969,10 +6031,6 @@ msgstr "Negative Funding Fee" msgid "{fromText} to {toExecutionText}" msgstr "{fromText} to {toExecutionText}" -#: src/components/PositionSeller/PositionSeller.tsx -msgid "Create {0} Order" -msgstr "Create {0} Order" - #: src/context/SubaccountContext/SubaccountContextProvider.tsx msgid "Settings updated." msgstr "Settings updated." @@ -6050,11 +6108,6 @@ msgstr "Swap {fromAmount} for {toAmount}" msgid "Amount" msgstr "Amount" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create {0} order" -msgstr "Create {0} order" - #: src/components/PositionEditor/PositionEditor.tsx msgid "Edit {0} {1}{2}" msgstr "Edit {0} {1}{2}" @@ -6136,6 +6189,7 @@ msgstr "There may not be sufficient liquidity to execute your order when the min #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -6242,6 +6296,7 @@ msgstr "Sell Fee" #: src/components/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Execution Details" msgstr "Execution Details" @@ -6266,7 +6321,7 @@ msgstr "Update Take Profit" msgid "Only addresses with over {0} in capital used are ranked.<0/><1/>The capital used is calculated as the highest value of [<2>sum of collateral of open positions - realized PnL + period start pending PnL]." msgstr "Only addresses with over {0} in capital used are ranked.<0/><1/>The capital used is calculated as the highest value of [<2>sum of collateral of open positions - realized PnL + period start pending PnL]." -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Active Orders" msgstr "Active Orders" @@ -6317,6 +6372,10 @@ msgstr "Protocol analytics" msgid "More active management required compared to GLV" msgstr "More active management required compared to GLV" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Take Profit Price" +msgstr "Take Profit Price" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed amount" msgstr "Fees exceed amount" @@ -6419,6 +6478,10 @@ msgstr "Kudai AI Agent" msgid "Current Price" msgstr "Current Price" +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +msgid "Enter TP/SL price" +msgstr "Enter TP/SL price" + #: src/pages/NftWallet/NftWallet.jsx msgid "Enter NFT ID" msgstr "Enter NFT ID" @@ -6465,10 +6528,6 @@ msgstr "Deposit" msgid "365d Est. Fees" msgstr "365d Est. Fees" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below highest limit price" -msgstr "Trigger price below highest limit price" - #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/ClaimModal.tsx msgid "Select rewards to claim" msgstr "Select rewards to claim" @@ -6536,7 +6595,9 @@ msgstr "Claim <0>{0}" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx @@ -6581,7 +6642,9 @@ msgid "We Value Your Feedback" msgstr "We Value Your Feedback" #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Stop Loss" msgstr "Stop Loss" @@ -6634,6 +6697,10 @@ msgstr "Error submitting order" msgid "Duration" msgstr "Duration" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "TP Price" +msgstr "TP Price" + #: src/components/Seo/SEO.tsx msgid "Trade spot or perpetual BTC, ETH, AVAX and other top cryptocurrencies with up to 100x leverage directly from your wallet on Arbitrum and Avalanche." msgstr "Trade spot or perpetual BTC, ETH, AVAX and other top cryptocurrencies with up to 100x leverage directly from your wallet on Arbitrum and Avalanche." @@ -6790,7 +6857,8 @@ msgstr "GMX rewards:" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderItem/OrderItem.tsx #: src/components/OrderList/OrderList.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx msgid "Trigger Price" @@ -7025,6 +7093,10 @@ msgstr "shift" msgid "Why is the claim in GLV tokens?" msgstr "Why is the claim in GLV tokens?" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PNL" +msgstr "Est. PNL" + #: src/components/SettingsModal/TradingSettings.tsx msgid "Express + One-Click" msgstr "Express + One-Click" @@ -7091,6 +7163,8 @@ msgstr "Shift error." #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -7109,7 +7183,7 @@ msgstr "Long" #: src/components/PoolSelector2/PoolSelector2.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx -#: src/components/TradeBox/MarketPoolSelectorRow.tsx +#: src/components/TradeBox/MarketPoolSelectorField.tsx msgid "Pool" msgstr "Pool" @@ -7202,6 +7276,8 @@ msgstr "SNAPSHOT" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Liquidation Price" msgstr "Liquidation Price" @@ -7259,6 +7335,7 @@ msgstr "Confirming..." msgid "Deposited:" msgstr "Deposited:" +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Increase" @@ -7384,6 +7461,10 @@ msgstr "Completed" msgid "Referral Terms" msgstr "Referral Terms" +#: src/components/PositionDropdown/PositionDropdown.tsx +msgid "Increase Size (TWAP)" +msgstr "Increase Size (TWAP)" + #: src/components/Earn/Portfolio/RecommendedAssets/RecommendedAssets.tsx #: src/components/GmList/FeeApyLabel.tsx msgid "Fee APY" @@ -7394,7 +7475,6 @@ msgid "Can I build on top of GMX or integrate it into my DeFi app?" msgstr "Can I build on top of GMX or integrate it into my DeFi app?" #: src/components/GmSwap/GmSwapBox/GmSwapWarningsRow.tsx -#: src/components/TradeBox/TradeBox.tsx msgid "Support for GMX accounts on Avalanche will be discontinued soon. Opening new positions from and depositing additional funds to Avalanche GMX accounts is no longer available. We recommend switching to Arbitrum as a settlement network." msgstr "Support for GMX accounts on Avalanche will be discontinued soon. Opening new positions from and depositing additional funds to Avalanche GMX accounts is no longer available. We recommend switching to Arbitrum as a settlement network." @@ -7437,6 +7517,10 @@ msgstr "Only letters, numbers and underscores are allowed." msgid "High TWAP network fee" msgstr "High TWAP network fee" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the TP/SL button to set TP/SL orders." +msgstr "Use the TP/SL button to set TP/SL orders." + #: src/components/Earn/Discovery/EarnProductCard.tsx msgid "What is GM?" msgstr "What is GM?" @@ -7562,6 +7646,7 @@ msgstr "<0>every {minutes} minutes{0}" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/EntryPriceRow.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Entry Price" @@ -7636,10 +7721,6 @@ msgstr "Convert esGMX tokens to GMX tokens. Please read the vesting details befo msgid "You have an existing position in the {0} market pool.<0>Switch to {1} market pool" msgstr "You have an existing position in the {0} market pool.<0>Switch to {1} market pool" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Stop Loss PnL" -msgstr "Stop Loss PnL" - #: src/components/TVChartContainer/constants.ts msgid "Market - Long Dec." msgstr "Market - Long Dec." @@ -7787,6 +7868,7 @@ msgstr "Claim funds" msgid "30d Fee APY" msgstr "30d Fee APY" +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "Net Price Impact / Fees" msgstr "Net Price Impact / Fees" @@ -7858,6 +7940,7 @@ msgstr "Generate Referral Code" #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/SwapCard/SwapCard.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "Swap" msgstr "Swap" @@ -7896,10 +7979,6 @@ msgstr "Language" msgid "Complete Transfer" msgstr "Complete Transfer" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below lowest limit price" -msgstr "Trigger price below lowest limit price" - #: src/components/Referrals/TradersStats.tsx msgid "GMX V2 discounts are automatically applied on each trade and are not displayed on this table." msgstr "GMX V2 discounts are automatically applied on each trade and are not displayed on this table." @@ -7976,10 +8055,6 @@ msgstr "Last 30d" msgid "Dashboards" msgstr "Dashboards" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create TWAP {0} order" -msgstr "Create TWAP {0} order" - #: src/components/AddressDropdown/AddressDropdownWithoutMultichain.tsx msgid "Address copied to your clipboard." msgstr "Address copied to your clipboard." @@ -8006,6 +8081,10 @@ msgstr "Open App" msgid "7d" msgstr "7d" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "TP/SL fees" +msgstr "TP/SL fees" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "Rage Trade" msgstr "Rage Trade" @@ -8032,6 +8111,7 @@ msgstr "Transfer failed." #: src/components/OrderList/OrderList.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Mark Price" @@ -8153,7 +8233,7 @@ msgstr "Very likely" msgid "Withdrawing funds from GMX Account..." msgstr "Withdrawing funds from GMX Account..." -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing limit order with {symbol} as collateral. <0>Switch to {symbol} collateral" msgstr "You have an existing limit order with {symbol} as collateral. <0>Switch to {symbol} collateral" @@ -8272,7 +8352,6 @@ msgstr "Loading" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/TradeBox/tradeboxConstants.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx #: src/components/TradeHistory/keys.ts #: src/domain/synthetics/positions/utils.ts msgid "Limit" @@ -8324,10 +8403,9 @@ msgid "multiple asset classes" msgstr "multiple asset classes" #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/multichain/progress/MultichainTransferProgressView.tsx diff --git a/src/locales/es/messages.po b/src/locales/es/messages.po index b51ebe6665..6807453d0e 100644 --- a/src/locales/es/messages.po +++ b/src/locales/es/messages.po @@ -73,6 +73,11 @@ msgstr "PRECIO" msgid "receive" msgstr "recibir" +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx +msgid "Create basic TP/SL orders that fully close your position. For advanced TP/SL setup, use the positions list after opening a position." +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Referrals Dashboard" msgstr "Panel de Referidos GMX" @@ -133,7 +138,7 @@ msgstr "Optimizador de rendimientos en Avalanche" msgid "Arbitrum" msgstr "Arbitrum" -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Orders ({0})" msgstr "Órdenes ({0})" @@ -148,6 +153,8 @@ msgstr "stop loss" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/Referrals/AffiliatesStats.tsx #: src/components/Referrals/TradersStats.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx msgid "Type" @@ -264,6 +271,10 @@ msgstr "Ejecutar Intercambio de Mercado" msgid "Discover" msgstr "Descubrir" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Full Position Close" +msgstr "" + #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "There is no price impact for increase orders, orders are filled at the mark price. <0>Read more." msgstr "No hay impacto en el precio para órdenes de aumento; las órdenes se ejecutan al precio mark. <0>Más información." @@ -428,7 +439,6 @@ msgstr "USD de reserva máxima superado" #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price below mark price" @@ -641,6 +651,10 @@ msgstr "Contacto de Telegram (opcional)" msgid "Market order will be cancellable in {minutesText}{seconds}s." msgstr "La orden de mercado será cancelable en {minutesText}{seconds}s." +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Edit TP/SL Size" +msgstr "" + #: src/components/UserIncentiveDistribution/AboutGlpIncident.tsx msgid "About GLP Incident" msgstr "Acerca del Incidente de GLP" @@ -675,17 +689,19 @@ msgid "Settling" msgstr "Liquidando" #: src/components/PositionDropdown/PositionDropdown.tsx -#: src/components/PositionItem/PositionItem.tsx msgid "Edit Collateral" msgstr "Editar Garantía" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "SL Price" +msgstr "" + #: src/components/Earn/Discovery/EarnFaq.tsx msgid "GLV and GM both enable liquidity provision on GMX, allowing users to earn fees from three revenue sources that auto-compound into the token's price. GM tokens represent liquidity in a single pool that supports one specific market on GMX. GLV vaults contain multiple GM tokens in an auto-rebalancing composition, thereby aggregating liquidity across various markets that have the same backing tokens (ex: ETH-USDC, or BTC-USDC). GM enables users to provide isolated liquidity with exposure to a specific market, while GLV allows users to passively earn yield from a diverse range of highly utilized pools." msgstr "Tanto GLV como GM permiten proveer liquidez en GMX y ganar comisiones de tres fuentes de ingresos que se autocompound en el precio del token. Los tokens GM representan liquidez en un único pool que soporta un mercado específico en GMX. Los vaults GLV contienen múltiples tokens GM con rebalanceo automático, agregando liquidez entre varios mercados con los mismos tokens de respaldo (ej: ETH-USDC o BTC-USDC). GM permite proveer liquidez aislada con exposición a un mercado concreto, mientras que GLV permite ganar rendimiento de forma pasiva desde una amplia variedad de pools altamente utilizados." #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price above mark price" @@ -862,6 +878,10 @@ msgstr "Retirar staking de {tokenSymbol}" msgid "MAX" msgstr "MÁX" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Stop Loss Price" +msgstr "" + #: src/pages/BuyGMX/BuyGMX.tsx msgid "No centralized exchanges available for this network." msgstr "No hay exchanges centralizados disponibles para esta red." @@ -929,6 +949,8 @@ msgstr "Estás a punto de navegar a GMX Solana, que es operado por un equipo sep #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx msgid "Pay" msgstr "Pagar" @@ -1111,6 +1133,7 @@ msgstr "Permitir que todos mis tokens sean transferidos a una nueva cuenta" #: src/components/OrderItem/OrderItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Collateral" msgstr "Garantía" @@ -1316,6 +1339,10 @@ msgstr "Orden mín.: {0}" msgid "Wallet is not connected" msgstr "Wallet no conectada" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of position)" +msgstr "" + #: landing/src/pages/Home/HeroSection/AnimatedTitle.tsx msgid "100+ crypto tokens" msgstr "Más de 100 tokens cripto" @@ -1338,6 +1365,10 @@ msgstr "GM es el token de proveedor de liquidez para mercados GMX V2. Acumula 63 msgid "The current price impact is {0}. Consider adding a buffer of 0.30% to it so the order is more likely to be processed." msgstr "El impacto de precio actual es {0}. Considera agregar un buffer de 0.30% para que la orden sea más probable que se procese." +#: src/components/TPSLModal/TPSLModal.tsx +msgid "TP/SL: {positionTitle}" +msgstr "" + #: src/components/SettingsModal/RpcDebugSettings.tsx msgid "Public" msgstr "Público" @@ -1444,6 +1475,10 @@ msgstr "Marca temporal" msgid "The amount you are trying to withdraw exceeds the limit. Please try an amount smaller than <0>{upperLimitFormatted}." msgstr "La cantidad que intentas retirar excede el límite. Por favor, intenta una cantidad menor que <0>{upperLimitFormatted}." +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Gain" +msgstr "" + #: src/components/NotifyModal/NotifyModal.tsx msgid "Discover GMX Alerts" msgstr "Descubre Alertas GMX" @@ -1668,10 +1703,6 @@ msgstr "No se encontró ruta de intercambio" msgid "RPnL" msgstr "RPnL" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined stop losses are at maximum (100%). Decrease existing values to add more orders." -msgstr "Los stop losses combinados están al máximo (100%). Reduce valores existentes para agregar más órdenes." - #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx msgid "4-18 Places" @@ -1768,7 +1799,6 @@ msgid "Min size per part: {0}" msgstr "Tamaño mín. por parte: {0}" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price above liq. price" msgstr "Precio de activación por encima del precio de liq." @@ -1800,7 +1830,6 @@ msgstr "Bóvedas Basadas en Opciones" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionSeller/PositionSeller.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Mark" @@ -1942,6 +1971,7 @@ msgstr "El valor USD de las comisiones se calcula en el momento en que se ganan #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeboxMarginFields/MarginPercentageSlider.tsx msgid "Max" msgstr "Máx." @@ -2020,9 +2050,9 @@ msgstr "Comprar GMX en {chainName}" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx +#: src/components/TradeboxMarginFields/SizeField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -2174,6 +2204,7 @@ msgstr "Endpoints actuales ({0})" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Initial collateral (collateral excluding borrow and funding fee)." msgstr "Colateral inicial (colateral excluyendo préstamo y tarifa de funding)." @@ -2435,10 +2466,6 @@ msgstr "Puente y intercambio" msgid "The execution price may vary from your set limit price due to fees and price impact, ensuring you receive the displayed minimum receive amount. <0>Read more." msgstr "El precio de ejecución puede variar respecto al precio límite establecido debido a comisiones e impacto en el precio, asegurando que recibas el monto mínimo mostrado. <0>Más información." -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above lowest limit price" -msgstr "Precio de activación por encima del precio límite más bajo" - #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2528,6 +2555,10 @@ msgstr "Los APRs se actualizan semanalmente los miércoles y dependerán de las msgid "Fees (Incl. Swap)" msgstr "Comisiones (Incl. Intercambio)" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "Order update fee" +msgstr "" + #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts msgid "{fromText} to {toMinText}" @@ -2614,6 +2645,7 @@ msgid "Exposure to Backing Tokens" msgstr "Exposición a Tokens de Respaldo" #: src/components/Referrals/AddAffiliateCode.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Creating..." msgstr "Creando…" @@ -2669,7 +2701,9 @@ msgstr "En otros tokens:" msgid "Returned Collateral" msgstr "Garantía Devuelta" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "There are issues in the TP/SL orders." msgstr "Hay problemas en las órdenes TP/SL." @@ -2762,10 +2796,6 @@ msgstr "Comprar GMX en exchanges centralizados:" msgid "GMX bonds can be bought on Bond Protocol with a discount and a small vesting period:" msgstr "Bonos GMX pueden comprarse en Bond Protocol con descuento y un pequeño período de adquisición:" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above highest limit price" -msgstr "Precio de activación por encima del precio límite más alto" - #: src/components/Earn/Discovery/EarnFaq.tsx msgid "Yes, staking and providing liquidity are fully permissionless. The GMX, GLV, and GM tokens are liquid and can be unstaked and withdrawn at any time. For LP tokens, there could be some rare edge cases where your liquidity is being used for active trades. In that case, you can withdraw as soon as positions close." msgstr "Sí, el staking y la provisión de liquidez son completamente sin permisos. Los tokens GMX, GLV y GM son líquidos y pueden retirarse del staking en cualquier momento. En casos excepcionales con tokens LP, si tu liquidez está siendo utilizada en operaciones abiertas, podrás retirarla en cuanto se cierren las posiciones." @@ -2895,12 +2925,16 @@ msgstr "<0>Vendiendo {0}{1}<1>{poolName}" msgid "No eligible tokens available on {0} for deposit" msgstr "No hay tokens elegibles disponibles en {0} para depósito" +#: src/components/TPSLModal/TPSLInputRow.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PnL" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Max close amount exceeded" msgstr "Cantidad máxima de cierre excedida" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2986,6 +3020,7 @@ msgstr "20s" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx @@ -3025,7 +3060,8 @@ msgstr "Liquidez disponible insuficiente. La orden se llenará cuando haya liqui #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Claims/ClaimsHistory.tsx #: src/components/OrderItem/OrderItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/Referrals/ClaimAffiliatesModal/ClaimAffiliatesModal.tsx #: src/components/TableMarketFilter/MarketFilterBase.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx @@ -3114,7 +3150,6 @@ msgid "Debug values are not available" msgstr "Valores de depuración no disponibles" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price below liq. price" msgstr "Precio de activación por debajo del precio de liq." @@ -3247,10 +3282,6 @@ msgstr "Tamaño Prom." msgid "V1 esGMX" msgstr "V1 esGMX" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Take Profit PnL" -msgstr "GyP de Take Profit" - #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your GLV or GM tokens, and borrow against them, or put your LP tokens into strategies to maximize your yield." msgstr "Presta tus tokens GLV o GM, pide prestado contra ellos o coloca tus tokens LP en estrategias para maximizar tu rendimiento." @@ -3295,6 +3326,7 @@ msgstr "Reclamando..." #: src/components/Earn/AdditionalOpportunities/OpportunityFilters.tsx #: src/components/GmxAccountModal/AvailableToTradeAssetsView.tsx #: src/components/TokenSelector/MultichainMarketTokenSelector.tsx +#: src/components/TPSLModal/TPSLModal.tsx msgid "All" msgstr "Todos" @@ -3321,7 +3353,8 @@ msgstr "Posiciones {longOrShort} {fundingAction} una comisión de financiación #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/domain/synthetics/positions/utils.ts msgid "TWAP" @@ -3401,10 +3434,6 @@ msgstr "Deslizamiento Permitido" msgid "{longOrShort} positions {fundingAction} a funding fee of {fundingRate} per hour and do not pay a borrow fee." msgstr "Posiciones {longOrShort} {fundingAction} una comisión de financiación de {fundingRate} por hora y no pagan comisión de préstamo." -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Short TP/SL: {0} a short position when the trigger price is reached." -msgstr "{typeString} Corto TP/SL: {0} una posición corta cuando se alcanza el precio de activación." - #: src/pages/Ecosystem/Ecosystem.tsx msgid "Community-led Telegram groups." msgstr "Grupos de Telegram liderados por la comunidad." @@ -3438,6 +3467,10 @@ msgstr "Solicitar Intercambio de Mercado" msgid "Insufficient liquidity in the {0} market pool. Select a different pool for this market.{1}" msgstr "Liquidez insuficiente en la reserva de mercado {0}. Selecciona una reserva diferente para este mercado.{1}" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Loss" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "App disabled, pending {0} upgrade" msgstr "App deshabilitada, pendiente actualización de {0}" @@ -3472,6 +3505,10 @@ msgstr "Éxito reclamando tarifas de funding" msgid "<0>every {hours} hours{0}" msgstr "<0>cada {hours} horas{0}" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "{secondLabel}" +msgstr "" + #: src/components/DebugExpressSettings/DebugSwapsSettings.tsx #: src/components/DebugSwapsSettings/DebugSwapsSettings.tsx msgid "Fail External Swaps" @@ -3501,13 +3538,17 @@ msgstr "Intercambio enviado." #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/pages/AccountDashboard/DailyAndCumulativePnL.tsx msgid "PnL" msgstr "GyP" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "No TP/SL orders" +msgstr "" + #: src/components/TradeBox/ExpressTradingWarningCard.tsx msgid "Express Trading is not available using network's native token {0}. Consider using {1} instead." msgstr "Trading Express no está disponible usando el token nativo de la red {0}. Considera usar {1} en su lugar." @@ -3773,7 +3814,6 @@ msgstr "Ganar" #: src/components/CollateralSelector/CollateralSelector.tsx #: src/components/CollateralSelector/PositionEditorCollateralSelector.tsx -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx msgid "Collateral In" msgstr "Garantía En" @@ -3785,6 +3825,10 @@ msgstr "Mercados GM" msgid "Your opinions and experiences matter to us. Your feedback helps us understand what we are doing well and where we can make enhancements." msgstr "Tus opiniones y experiencias nos importan. Tu retroalimentación nos ayuda a entender qué hacemos bien y dónde podemos mejorar." +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Create TP/SL" +msgstr "" + #: src/components/GmxAccountModal/DepositView.tsx #: src/components/GmxAccountModal/WithdrawalView.tsx msgid "Estimated Time" @@ -3823,6 +3867,10 @@ msgstr "Reembolsos ganados por esta cuenta como trader." msgid "Transfer account" msgstr "Transferir cuenta" +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Cancel all" +msgstr "" + #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your tokens, or borrow against it." @@ -3845,8 +3893,6 @@ msgstr "<0>GMX ejecuta operaciones contra un pool de liquidez equilibrado dinám #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx msgid "Read more" msgstr "Leer más" @@ -3915,6 +3961,7 @@ msgstr "Explorar más" #: src/components/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/AllowedSwapSlippageInputRowImpl/AllowedSwapSlippageInputRowImpl.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/pages/LeaderboardPage/components/LeaderboardAccountsTable.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -3941,6 +3988,8 @@ msgstr "Precio de activación para la orden." #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -4009,10 +4058,6 @@ msgstr "Alertas de Precio de Par de Trading" msgid "Links" msgstr "Enlaces" -#: src/components/PositionItem/PositionItem.tsx -msgid "Use the \"Close\" button to reduce your position via market, TP/SL, or TWAP orders." -msgstr "Usa el botón «Cerrar» para reducir tu posición mediante órdenes de mercado, TP/SL o TWAP." - #: src/components/Seo/SEO.tsx msgid "GMX | Decentralized Perpetual Exchange" msgstr "GMX | Exchange Perpetuo Descentralizado" @@ -4227,7 +4272,7 @@ msgstr "Empleos" msgid "Decentralized Options Strategies" msgstr "Estrategias de Opciones Descentralizadas" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This action will not apply for that position. <0>Switch to {1} collateral" msgstr "Tienes una posición existente con {0} como colateral. Esta acción no aplicará para esa posición. <0>Cambia a colateral {1}" @@ -4267,7 +4312,9 @@ msgid "Amount of traders you referred." msgstr "Cantidad de traders que has referido." #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Take Profit" msgstr "Take Profit" @@ -4285,6 +4332,14 @@ msgstr "{typeString} Long Stop Market: {0} una posición larga cuando el precio msgid "Insufficient {0} liquidity" msgstr "Liquidez de {0} insuficiente" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Unable to calculate order" +msgstr "" + +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "TP/SL: {positionTitle} Decrease" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.tsx #: src/pages/Ecosystem/Ecosystem.tsx msgid "Creator" @@ -4310,6 +4365,10 @@ msgstr "Crear Límite" msgid "Transfer already initiated" msgstr "Transferencia ya iniciada" +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +msgid "Margin to Pay" +msgstr "" + #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx msgid "Withdraw submitted." msgstr "Retiro enviado." @@ -4387,7 +4446,8 @@ msgstr "Botanix" msgid "Accrued price impact rebates. They will become claimable after 5 days.<0/><1/><2>Read more." msgstr "Reembolsos de impacto de precio acumulados. Serán reclamables después de 5 días.<0/><1/><2>Leer más." -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "Take Profit / Stop Loss" msgstr "Take Profit / Stop Loss" @@ -4483,6 +4543,10 @@ msgstr "No se encontraron mercados." msgid "RPC Monitoring" msgstr "Monitoreo RPC" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the \"Close\" button to reduce your position via market or TWAP orders." +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "ODOS" msgstr "ODOS" @@ -4544,10 +4608,6 @@ msgstr "Alto impacto en el precio" msgid "<0>Solana Support<1>Botanix Support<2>GMX Express" msgstr "<0>Soporte para Solana<1>Soporte para Botanix<2>GMX Express" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined take profits are at maximum (100%). Decrease existing values to add more orders." -msgstr "Take profits combinados están al máximo (100%). Reduce valores existentes para agregar más órdenes." - #: src/components/GmList/GmList.tsx msgid "POOL" msgstr "RESERVA" @@ -4580,6 +4640,8 @@ msgstr "Vendiendo" msgid "Recommended" msgstr "Recomendado" +#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Decrease" @@ -4783,11 +4845,12 @@ msgstr "V1 Avalanche" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Collateral ({0})" msgstr "Garantía ({0})" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This order will not be valid for that position. <0>Switch to {1} collateral" msgstr "Tienes una posición existente con {0} como colateral. Esta orden no será válida para esa posición. <0>Cambiar a colateral {1}" @@ -4890,10 +4953,6 @@ msgstr "Error de orden. Los precios son actualmente volátiles para este mercado msgid "Swap Price Impact for External Swap Threshold" msgstr "Impacto de Precio de Intercambio para Umbral de Intercambio Externo" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Limit / Take Profit / Stop Loss" -msgstr "Límite / Take Profit / Stop Loss" - #: landing/src/pages/Home/LiqiuditySection/PoolCards.tsx msgid "Steady returns without management" msgstr "Retornos estables sin gestión" @@ -4906,10 +4965,6 @@ msgstr "Deslizamiento" msgid "This position was liquidated as the max. leverage of {formattedMaxLeverage} was exceeded when taking into account fees." msgstr "Esta posición fue liquidada debido a que el apalancamiento máximo de {formattedMaxLeverage} fue superado al tener en cuenta las comisiones." -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Swap {0}" -msgstr "Intercambiar {0}" - #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx @@ -4999,10 +5054,6 @@ msgstr "APR de wstETH" msgid "Telegram Group" msgstr "Grupo de Telegram" -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Long TP/SL: {0} a long position when the trigger price is reached." -msgstr "{typeString} Largo TP/SL: {0} una posición larga cuando se alcanza el precio de activación." - #: src/components/OrderItem/TwapOrdersList/TwapOrdersList.tsx msgid "<0>{fromTokenText} {fromTokenIcon}<1> to {toTokenIcon}" msgstr "<0>{fromTokenText} {fromTokenIcon}<1> a {toTokenIcon}" @@ -5031,7 +5082,7 @@ msgstr "" #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/GmxAssetCard.tsx #: src/components/MarketStats/MarketGraphs.tsx #: src/components/SwapCard/SwapCard.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TradeboxMarginFields/PriceField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TVChart/Chart.tsx #: src/pages/Dashboard/GmxCard.tsx @@ -5194,6 +5245,10 @@ msgstr "Hash TX del puente" msgid "Start unrealized pnl" msgstr "PnL no realizado inicial" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of Position)" +msgstr "" + #: src/domain/tokens/approveTokens.tsx msgid "Approval submitted! <0>View status." msgstr "¡Aprobación enviada! <0>Ver estado." @@ -5215,7 +5270,7 @@ msgid "Stake GMX to earn GMX rewards" msgstr "Haz staking de GMX para ganar recompensas GMX" #: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionList/PositionList.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "TP/SL" msgstr "TP/SL" @@ -5443,6 +5498,7 @@ msgstr "Gobernanza" #: src/components/OrderItem/OrderItem.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Cancel" msgstr "Cancelar" @@ -5509,6 +5565,11 @@ msgstr "90d" msgid "Liquidation" msgstr "Liquidación" +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Add TP/SL" +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Risk Monitoring" msgstr "Monitoreo de Riesgos GMX" @@ -5553,6 +5614,7 @@ msgid "Longs Net Rate / 1h" msgstr "Largos Tasa Neta / 1h" #: src/components/OrderItem/OrderItem.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx msgid "Edit" msgstr "Editar" @@ -5565,10 +5627,6 @@ msgstr "No se encontró apalancamiento disponible" msgid "You have a <0>pending transfer to {pendingReceiver}." msgstr "Tienes una <0>transferencia pendiente hacia {pendingReceiver}." -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Limit size is required" -msgstr "Se requiere tamaño límite" - #: src/components/GmxAccountModal/DepositView.tsx msgid "From Network" msgstr "Desde Red" @@ -5585,6 +5643,10 @@ msgstr "Retirar de Cuenta GMX" msgid "Collateral at Liquidation" msgstr "Garantía en Liquidación" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Keep leverage at {currentLeverage}" +msgstr "" + #: src/lib/dates.ts #: src/pages/AccountDashboard/GeneralPerformanceDetails.tsx msgid "Today" @@ -5969,10 +6031,6 @@ msgstr "Tarifa de Funding Negativa" msgid "{fromText} to {toExecutionText}" msgstr "{fromText} a {toExecutionText}" -#: src/components/PositionSeller/PositionSeller.tsx -msgid "Create {0} Order" -msgstr "Crear Orden {0}" - #: src/context/SubaccountContext/SubaccountContextProvider.tsx msgid "Settings updated." msgstr "Configuración actualizada." @@ -6050,11 +6108,6 @@ msgstr "Intercambiar {fromAmount} por {toAmount}" msgid "Amount" msgstr "Cantidad" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create {0} order" -msgstr "Crear {0} orden" - #: src/components/PositionEditor/PositionEditor.tsx msgid "Edit {0} {1}{2}" msgstr "Editar {0} {1}{2}" @@ -6136,6 +6189,7 @@ msgstr "Puede no haber liquidez suficiente para ejecutar tu orden cuando se cump #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -6242,6 +6296,7 @@ msgstr "Comisión de Venta" #: src/components/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Execution Details" msgstr "Detalles de Ejecución" @@ -6266,7 +6321,7 @@ msgstr "Actualizar Take Profit" msgid "Only addresses with over {0} in capital used are ranked.<0/><1/>The capital used is calculated as the highest value of [<2>sum of collateral of open positions - realized PnL + period start pending PnL]." msgstr "Solo las direcciones con más de {0} en capital utilizado son clasificadas.<0/><1/>El capital utilizado se calcula como el valor más alto de [<2>suma del colateral de posiciones abiertas - PnL realizado + PnL pendiente al inicio del período]." -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Active Orders" msgstr "Órdenes Activas" @@ -6317,6 +6372,10 @@ msgstr "Análisis de datos del protocolo" msgid "More active management required compared to GLV" msgstr "Requiere más gestión activa en comparación con GLV" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Take Profit Price" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed amount" msgstr "Comisiones exceden la cantidad" @@ -6419,6 +6478,10 @@ msgstr "Agente AI Kudai" msgid "Current Price" msgstr "Precio Actual" +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +msgid "Enter TP/SL price" +msgstr "" + #: src/pages/NftWallet/NftWallet.jsx msgid "Enter NFT ID" msgstr "Introduce tu ID de NFT" @@ -6465,10 +6528,6 @@ msgstr "Depositar" msgid "365d Est. Fees" msgstr "Comisiones Est. 365d" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below highest limit price" -msgstr "Precio de activación por debajo del precio límite más alto" - #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/ClaimModal.tsx msgid "Select rewards to claim" msgstr "Seleccionar recompensas a reclamar" @@ -6536,7 +6595,9 @@ msgstr "Reclamar <0>{0}" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx @@ -6581,7 +6642,9 @@ msgid "We Value Your Feedback" msgstr "Valoramos Tu Feedback" #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Stop Loss" msgstr "Stop Loss" @@ -6634,6 +6697,10 @@ msgstr "Error al enviar la orden" msgid "Duration" msgstr "Duración" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "TP Price" +msgstr "" + #: src/components/Seo/SEO.tsx msgid "Trade spot or perpetual BTC, ETH, AVAX and other top cryptocurrencies with up to 100x leverage directly from your wallet on Arbitrum and Avalanche." msgstr "Opera spot o perpetuos BTC, ETH, AVAX y otras criptomonedas top con hasta 100x apalancamiento directamente desde tu wallet en Arbitrum y Avalanche." @@ -6790,7 +6857,8 @@ msgstr "Recompensas GMX:" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderItem/OrderItem.tsx #: src/components/OrderList/OrderList.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx msgid "Trigger Price" @@ -7025,6 +7093,10 @@ msgstr "cambio" msgid "Why is the claim in GLV tokens?" msgstr "¿Por qué la reclamación es en tokens GLV?" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PNL" +msgstr "" + #: src/components/SettingsModal/TradingSettings.tsx msgid "Express + One-Click" msgstr "Express + Un Clic" @@ -7091,6 +7163,8 @@ msgstr "Error de cambio." #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -7109,7 +7183,7 @@ msgstr "Largo" #: src/components/PoolSelector2/PoolSelector2.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx -#: src/components/TradeBox/MarketPoolSelectorRow.tsx +#: src/components/TradeBox/MarketPoolSelectorField.tsx msgid "Pool" msgstr "Reserva" @@ -7202,6 +7276,8 @@ msgstr "SNAPSHOT" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Liquidation Price" msgstr "Precio de Liquidación" @@ -7259,6 +7335,7 @@ msgstr "Confirmando..." msgid "Deposited:" msgstr "Depositado:" +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Increase" @@ -7384,6 +7461,10 @@ msgstr "Completado" msgid "Referral Terms" msgstr "Términos de Referido" +#: src/components/PositionDropdown/PositionDropdown.tsx +msgid "Increase Size (TWAP)" +msgstr "" + #: src/components/Earn/Portfolio/RecommendedAssets/RecommendedAssets.tsx #: src/components/GmList/FeeApyLabel.tsx msgid "Fee APY" @@ -7394,7 +7475,6 @@ msgid "Can I build on top of GMX or integrate it into my DeFi app?" msgstr "¿Puedo construir sobre GMX o integrarlo en mi app DeFi?" #: src/components/GmSwap/GmSwapBox/GmSwapWarningsRow.tsx -#: src/components/TradeBox/TradeBox.tsx msgid "Support for GMX accounts on Avalanche will be discontinued soon. Opening new positions from and depositing additional funds to Avalanche GMX accounts is no longer available. We recommend switching to Arbitrum as a settlement network." msgstr "El soporte para cuentas GMX en Avalanche se discontinuará pronto. Abrir nuevas posiciones desde y depositar fondos adicionales en cuentas GMX de Avalanche ya no está disponible. Recomendamos cambiar a Arbitrum como red de liquidación." @@ -7437,6 +7517,10 @@ msgstr "Solo letras, números y guiones bajos están permitidos" msgid "High TWAP network fee" msgstr "Alta tarifa de red TWAP" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the TP/SL button to set TP/SL orders." +msgstr "" + #: src/components/Earn/Discovery/EarnProductCard.tsx msgid "What is GM?" msgstr "¿Qué es GM?" @@ -7562,6 +7646,7 @@ msgstr "<0>cada {minutes} minutos{0}" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/EntryPriceRow.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Entry Price" @@ -7636,10 +7721,6 @@ msgstr "Convierte tokens esGMX a tokens GMX. Lee los detalles del vesting antes msgid "You have an existing position in the {0} market pool.<0>Switch to {1} market pool" msgstr "Tienes una posición existente en el pool de mercado {0}.<0>Cambia a pool de mercado {1}" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Stop Loss PnL" -msgstr "PnL de Stop Loss" - #: src/components/TVChartContainer/constants.ts msgid "Market - Long Dec." msgstr "Mercado - Long Dec." @@ -7787,6 +7868,7 @@ msgstr "Reclamar fondos" msgid "30d Fee APY" msgstr "APY por comisiones (30 d)" +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "Net Price Impact / Fees" msgstr "Impacto de Precio Neto / Tarifas" @@ -7858,6 +7940,7 @@ msgstr "Generar Código de Referido" #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/SwapCard/SwapCard.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "Swap" msgstr "Intercambiar" @@ -7896,10 +7979,6 @@ msgstr "Idioma" msgid "Complete Transfer" msgstr "Completar transferencia" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below lowest limit price" -msgstr "Precio de activación por debajo del precio límite más bajo" - #: src/components/Referrals/TradersStats.tsx msgid "GMX V2 discounts are automatically applied on each trade and are not displayed on this table." msgstr "Los descuentos de GMX V2 se aplican automáticamente en cada operación y no se muestran en esta tabla." @@ -7976,10 +8055,6 @@ msgstr "Últimos 30d" msgid "Dashboards" msgstr "Paneles" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create TWAP {0} order" -msgstr "Crear orden TWAP {0}" - #: src/components/AddressDropdown/AddressDropdownWithoutMultichain.tsx msgid "Address copied to your clipboard." msgstr "Dirección copiada a tu portapapeles." @@ -8006,6 +8081,10 @@ msgstr "Abrir App" msgid "7d" msgstr "7d" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "TP/SL fees" +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "Rage Trade" msgstr "Rage Trade" @@ -8032,6 +8111,7 @@ msgstr "Transferencia fallida." #: src/components/OrderList/OrderList.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Mark Price" @@ -8153,7 +8233,7 @@ msgstr "Muy probable" msgid "Withdrawing funds from GMX Account..." msgstr "Retirando fondos de la cuenta GMX…" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing limit order with {symbol} as collateral. <0>Switch to {symbol} collateral" msgstr "Tienes una orden límite existente con {symbol} como colateral. <0>Cambia a colateral {symbol}" @@ -8272,7 +8352,6 @@ msgstr "Cargando" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/TradeBox/tradeboxConstants.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx #: src/components/TradeHistory/keys.ts #: src/domain/synthetics/positions/utils.ts msgid "Limit" @@ -8324,10 +8403,9 @@ msgid "multiple asset classes" msgstr "múltiples clases de activos" #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/multichain/progress/MultichainTransferProgressView.tsx diff --git a/src/locales/fr/messages.po b/src/locales/fr/messages.po index 6d2e7aa8ff..98f609be2a 100644 --- a/src/locales/fr/messages.po +++ b/src/locales/fr/messages.po @@ -73,6 +73,11 @@ msgstr "PRIX" msgid "receive" msgstr "recevoir" +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx +msgid "Create basic TP/SL orders that fully close your position. For advanced TP/SL setup, use the positions list after opening a position." +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Referrals Dashboard" msgstr "Tableau de bord des parrainages GMX" @@ -133,7 +138,7 @@ msgstr "Optimiseur de rendement sur Avalanche" msgid "Arbitrum" msgstr "Arbitrum" -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Orders ({0})" msgstr "Ordres ({0})" @@ -148,6 +153,8 @@ msgstr "stop loss" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/Referrals/AffiliatesStats.tsx #: src/components/Referrals/TradersStats.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx msgid "Type" @@ -264,6 +271,10 @@ msgstr "Exécuter l'échange de marché" msgid "Discover" msgstr "Découvrir" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Full Position Close" +msgstr "" + #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "There is no price impact for increase orders, orders are filled at the mark price. <0>Read more." msgstr "Il n'y a pas d'impact sur le prix pour les ordres d'augmentation ; les ordres sont exécutés au prix mark. <0>En savoir plus." @@ -428,7 +439,6 @@ msgstr "USD max du pool dépassé" #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price below mark price" @@ -641,6 +651,10 @@ msgstr "Contact Telegram (optionnel)" msgid "Market order will be cancellable in {minutesText}{seconds}s." msgstr "L'ordre de marché sera annulable dans {minutesText}{seconds}s." +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Edit TP/SL Size" +msgstr "" + #: src/components/UserIncentiveDistribution/AboutGlpIncident.tsx msgid "About GLP Incident" msgstr "À propos de l'Incident GLP" @@ -675,17 +689,19 @@ msgid "Settling" msgstr "Règlement" #: src/components/PositionDropdown/PositionDropdown.tsx -#: src/components/PositionItem/PositionItem.tsx msgid "Edit Collateral" msgstr "Modifier le collatéral" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "SL Price" +msgstr "" + #: src/components/Earn/Discovery/EarnFaq.tsx msgid "GLV and GM both enable liquidity provision on GMX, allowing users to earn fees from three revenue sources that auto-compound into the token's price. GM tokens represent liquidity in a single pool that supports one specific market on GMX. GLV vaults contain multiple GM tokens in an auto-rebalancing composition, thereby aggregating liquidity across various markets that have the same backing tokens (ex: ETH-USDC, or BTC-USDC). GM enables users to provide isolated liquidity with exposure to a specific market, while GLV allows users to passively earn yield from a diverse range of highly utilized pools." msgstr "GLV et GM permettent tous deux de fournir de la liquidité sur GMX, permettant aux utilisateurs de gagner des frais provenant de trois sources de revenus qui se recomposent automatiquement dans le prix du token. Les tokens GM représentent la liquidité dans un pool unique qui supporte un marché spécifique sur GMX. Les vaults GLV contiennent plusieurs tokens GM dans une composition auto-rééquilibrée, agrégeant ainsi la liquidité sur divers marchés ayant les mêmes tokens de backing (ex : ETH-USDC ou BTC-USDC). GM permet de fournir une liquidité isolée avec exposition à un marché spécifique, tandis que GLV permet de générer passivement du rendement à partir d'une gamme diversifiée de pools très utilisés." #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price above mark price" @@ -862,6 +878,10 @@ msgstr "Unstaker {tokenSymbol}" msgid "MAX" msgstr "MAX" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Stop Loss Price" +msgstr "" + #: src/pages/BuyGMX/BuyGMX.tsx msgid "No centralized exchanges available for this network." msgstr "Aucun exchange centralisé disponible pour ce réseau." @@ -929,6 +949,8 @@ msgstr "Vous êtes sur le point de naviguer vers GMX Solana, qui est opéré par #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx msgid "Pay" msgstr "Payer" @@ -1111,6 +1133,7 @@ msgstr "Autoriser le transfert de tous mes tokens vers un nouveau compte" #: src/components/OrderItem/OrderItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Collateral" msgstr "Collatéral" @@ -1316,6 +1339,10 @@ msgstr "Ordre min : {0}" msgid "Wallet is not connected" msgstr "Le portefeuille n’est pas connecté" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of position)" +msgstr "" + #: landing/src/pages/Home/HeroSection/AnimatedTitle.tsx msgid "100+ crypto tokens" msgstr "Plus de 100 tokens crypto" @@ -1338,6 +1365,10 @@ msgstr "GM est le token de fournisseur de liquidité pour les marchés GMX V2. A msgid "The current price impact is {0}. Consider adding a buffer of 0.30% to it so the order is more likely to be processed." msgstr "L'impact de prix actuel est {0}. Envisagez d'ajouter un buffer de 0.30% pour que l'ordre soit plus susceptible d'être traité." +#: src/components/TPSLModal/TPSLModal.tsx +msgid "TP/SL: {positionTitle}" +msgstr "" + #: src/components/SettingsModal/RpcDebugSettings.tsx msgid "Public" msgstr "Public" @@ -1444,6 +1475,10 @@ msgstr "Horodatage" msgid "The amount you are trying to withdraw exceeds the limit. Please try an amount smaller than <0>{upperLimitFormatted}." msgstr "Le montant que vous essayez de retirer dépasse la limite. Veuillez essayer un montant inférieur à <0>{upperLimitFormatted}." +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Gain" +msgstr "" + #: src/components/NotifyModal/NotifyModal.tsx msgid "Discover GMX Alerts" msgstr "Découvrir les alertes GMX" @@ -1668,10 +1703,6 @@ msgstr "Aucun chemin de swap trouvé" msgid "RPnL" msgstr "RPnL" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined stop losses are at maximum (100%). Decrease existing values to add more orders." -msgstr "Les stop losses combinés sont au maximum (100%). Diminuez les valeurs existantes pour ajouter plus d'ordres." - #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx msgid "4-18 Places" @@ -1768,7 +1799,6 @@ msgid "Min size per part: {0}" msgstr "Taille min par partie : {0}" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price above liq. price" msgstr "Prix déclencheur au-dessus du prix de liq." @@ -1800,7 +1830,6 @@ msgstr "Vaults basés sur options" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionSeller/PositionSeller.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Mark" @@ -1942,6 +1971,7 @@ msgstr "La valeur USD des frais est calculée au moment où ils sont gagnés et #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeboxMarginFields/MarginPercentageSlider.tsx msgid "Max" msgstr "Max" @@ -2020,9 +2050,9 @@ msgstr "Acheter GMX sur {chainName}" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx +#: src/components/TradeboxMarginFields/SizeField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -2174,6 +2204,7 @@ msgstr "Endpoints actuels ({0})" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Initial collateral (collateral excluding borrow and funding fee)." msgstr "Garantie initiale (garantie excluant les emprunts et frais de financement)." @@ -2435,10 +2466,6 @@ msgstr "Bridge et échange" msgid "The execution price may vary from your set limit price due to fees and price impact, ensuring you receive the displayed minimum receive amount. <0>Read more." msgstr "Le prix d'exécution peut varier par rapport à votre prix limite en raison des frais et de l'impact sur le prix, garantissant que vous recevez le montant minimum affiché. <0>En savoir plus." -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above lowest limit price" -msgstr "Prix de déclenchement supérieur au prix limite le plus bas" - #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2528,6 +2555,10 @@ msgstr "Les taux de rendement annualisés sont mis à jour chaque semaine le mer msgid "Fees (Incl. Swap)" msgstr "Frais (incl. échange)" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "Order update fee" +msgstr "" + #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts msgid "{fromText} to {toMinText}" @@ -2614,6 +2645,7 @@ msgid "Exposure to Backing Tokens" msgstr "Exposition aux tokens de soutien" #: src/components/Referrals/AddAffiliateCode.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Creating..." msgstr "Création en cours…" @@ -2669,7 +2701,9 @@ msgstr "En d'autres jetons :" msgid "Returned Collateral" msgstr "Collatéral retourné" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "There are issues in the TP/SL orders." msgstr "Il y a des problèmes dans les ordres TP/SL." @@ -2762,10 +2796,6 @@ msgstr "Acheter GMX sur des échanges centralisés :" msgid "GMX bonds can be bought on Bond Protocol with a discount and a small vesting period:" msgstr "Les obligations GMX peuvent être achetées sur Bond Protocol avec une remise et une petite période de vesting :" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above highest limit price" -msgstr "Prix de déclenchement supérieur au prix limite le plus élevé" - #: src/components/Earn/Discovery/EarnFaq.tsx msgid "Yes, staking and providing liquidity are fully permissionless. The GMX, GLV, and GM tokens are liquid and can be unstaked and withdrawn at any time. For LP tokens, there could be some rare edge cases where your liquidity is being used for active trades. In that case, you can withdraw as soon as positions close." msgstr "Oui, le staking et la fourniture de liquidité sont totalement sans permission. Les tokens GMX, GLV et GM sont liquides et peuvent être unstakés et retirés à tout moment. Pour les tokens LP, il peut exister de rares cas limites où votre liquidité est utilisée pour des trades actifs. Dans ce cas, vous pourrez retirer dès que les positions seront fermées." @@ -2895,12 +2925,16 @@ msgstr "<0>Vente de {0}{1}<1>{poolName}" msgid "No eligible tokens available on {0} for deposit" msgstr "Aucun jeton éligible disponible sur {0} pour dépôt" +#: src/components/TPSLModal/TPSLInputRow.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PnL" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Max close amount exceeded" msgstr "Montant de fermeture max dépassé" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2986,6 +3020,7 @@ msgstr "20s" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx @@ -3025,7 +3060,8 @@ msgstr "Liquidité disponible insuffisante. L'ordre sera exécuté lorsqu'il y a #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Claims/ClaimsHistory.tsx #: src/components/OrderItem/OrderItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/Referrals/ClaimAffiliatesModal/ClaimAffiliatesModal.tsx #: src/components/TableMarketFilter/MarketFilterBase.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx @@ -3114,7 +3150,6 @@ msgid "Debug values are not available" msgstr "Valeurs de débogage non disponibles" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price below liq. price" msgstr "Prix de déclenchement inférieur au prix de liq." @@ -3247,10 +3282,6 @@ msgstr "Taille moy." msgid "V1 esGMX" msgstr "V1 esGMX" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Take Profit PnL" -msgstr "PnL Take Profit" - #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your GLV or GM tokens, and borrow against them, or put your LP tokens into strategies to maximize your yield." msgstr "Prêtez vos tokens GLV ou GM, empruntez contre eux, ou placez vos tokens LP dans des stratégies pour maximiser votre rendement." @@ -3295,6 +3326,7 @@ msgstr "Réclamation en cours..." #: src/components/Earn/AdditionalOpportunities/OpportunityFilters.tsx #: src/components/GmxAccountModal/AvailableToTradeAssetsView.tsx #: src/components/TokenSelector/MultichainMarketTokenSelector.tsx +#: src/components/TPSLModal/TPSLModal.tsx msgid "All" msgstr "Tous" @@ -3321,7 +3353,8 @@ msgstr "Les positions {longOrShort} {fundingAction} des frais de financement de #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/domain/synthetics/positions/utils.ts msgid "TWAP" @@ -3401,10 +3434,6 @@ msgstr "Glissement autorisé" msgid "{longOrShort} positions {fundingAction} a funding fee of {fundingRate} per hour and do not pay a borrow fee." msgstr "Les positions {longOrShort} {fundingAction} des frais de financement de {fundingRate} par heure et ne paient pas de frais d'emprunt." -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Short TP/SL: {0} a short position when the trigger price is reached." -msgstr "{typeString} Short TP/SL : {0} une position short lorsque le prix de déclenchement est atteint." - #: src/pages/Ecosystem/Ecosystem.tsx msgid "Community-led Telegram groups." msgstr "Groupes Telegram gérés par la communauté." @@ -3438,6 +3467,10 @@ msgstr "Demander un échange de marché" msgid "Insufficient liquidity in the {0} market pool. Select a different pool for this market.{1}" msgstr "Liquidité insuffisante dans le pool de marché {0}. Sélectionnez un pool différent pour ce marché.{1}" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Loss" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "App disabled, pending {0} upgrade" msgstr "Application désactivée, en attente de mise à niveau {0}" @@ -3472,6 +3505,10 @@ msgstr "Succès de la réclamation des frais de financement" msgid "<0>every {hours} hours{0}" msgstr "<0>toutes les {hours} heures{0}" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "{secondLabel}" +msgstr "" + #: src/components/DebugExpressSettings/DebugSwapsSettings.tsx #: src/components/DebugSwapsSettings/DebugSwapsSettings.tsx msgid "Fail External Swaps" @@ -3501,13 +3538,17 @@ msgstr "Échange soumis." #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/pages/AccountDashboard/DailyAndCumulativePnL.tsx msgid "PnL" msgstr "PnL" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "No TP/SL orders" +msgstr "" + #: src/components/TradeBox/ExpressTradingWarningCard.tsx msgid "Express Trading is not available using network's native token {0}. Consider using {1} instead." msgstr "Le trading express n'est pas disponible en utilisant le token natif du réseau {0}. Envisagez d'utiliser {1} à la place." @@ -3773,7 +3814,6 @@ msgstr "Gagner" #: src/components/CollateralSelector/CollateralSelector.tsx #: src/components/CollateralSelector/PositionEditorCollateralSelector.tsx -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx msgid "Collateral In" msgstr "Collatéral En" @@ -3785,6 +3825,10 @@ msgstr "Marchés GM" msgid "Your opinions and experiences matter to us. Your feedback helps us understand what we are doing well and where we can make enhancements." msgstr "Vos opinions et expériences nous importent. Vos commentaires nous aident à comprendre ce que nous faisons bien et où nous pouvons nous améliorer." +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Create TP/SL" +msgstr "" + #: src/components/GmxAccountModal/DepositView.tsx #: src/components/GmxAccountModal/WithdrawalView.tsx msgid "Estimated Time" @@ -3823,6 +3867,10 @@ msgstr "Remises obtenues par ce compte en tant que trader." msgid "Transfer account" msgstr "Transférer le compte" +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Cancel all" +msgstr "" + #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your tokens, or borrow against it." @@ -3845,8 +3893,6 @@ msgstr "<0>GMX exécute les trades contre un pool de liquidité équilibré dyna #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx msgid "Read more" msgstr "Lire plus" @@ -3915,6 +3961,7 @@ msgstr "Explorer davantage" #: src/components/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/AllowedSwapSlippageInputRowImpl/AllowedSwapSlippageInputRowImpl.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/pages/LeaderboardPage/components/LeaderboardAccountsTable.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -3941,6 +3988,8 @@ msgstr "Prix de déclenchement pour l'ordre." #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -4009,10 +4058,6 @@ msgstr "Alertes de prix de paires de trading" msgid "Links" msgstr "Liens" -#: src/components/PositionItem/PositionItem.tsx -msgid "Use the \"Close\" button to reduce your position via market, TP/SL, or TWAP orders." -msgstr "Utilisez le bouton « Fermer » pour réduire votre position via des ordres de marché, TP/SL ou TWAP." - #: src/components/Seo/SEO.tsx msgid "GMX | Decentralized Perpetual Exchange" msgstr "GMX | Échange perpétuel décentralisé" @@ -4227,7 +4272,7 @@ msgstr "Emplois" msgid "Decentralized Options Strategies" msgstr "Stratégies d'options décentralisées" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This action will not apply for that position. <0>Switch to {1} collateral" msgstr "Vous avez une position existante avec {0} comme garantie. Cette action ne s'appliquera pas à cette position. <0>Passer à la garantie {1}" @@ -4267,7 +4312,9 @@ msgid "Amount of traders you referred." msgstr "Nombre de traders que vous avez parrainés." #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Take Profit" msgstr "Take Profit" @@ -4285,6 +4332,14 @@ msgstr "{typeString} Long Stop Market : {0} une position longue lorsque le prix msgid "Insufficient {0} liquidity" msgstr "Liquidité {0} insuffisante" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Unable to calculate order" +msgstr "" + +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "TP/SL: {positionTitle} Decrease" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.tsx #: src/pages/Ecosystem/Ecosystem.tsx msgid "Creator" @@ -4310,6 +4365,10 @@ msgstr "Créer Limite" msgid "Transfer already initiated" msgstr "Transfert déjà initié" +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +msgid "Margin to Pay" +msgstr "" + #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx msgid "Withdraw submitted." msgstr "Retrait soumis." @@ -4387,7 +4446,8 @@ msgstr "Botanix" msgid "Accrued price impact rebates. They will become claimable after 5 days.<0/><1/><2>Read more." msgstr "Remises d'impact de prix accumulées. Elles deviendront réclamables après 5 jours.<0/><1/><2>Lire plus." -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "Take Profit / Stop Loss" msgstr "Take Profit / Stop Loss" @@ -4483,6 +4543,10 @@ msgstr "Aucun marché trouvé." msgid "RPC Monitoring" msgstr "Surveillance RPC" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the \"Close\" button to reduce your position via market or TWAP orders." +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "ODOS" msgstr "ODOS" @@ -4544,10 +4608,6 @@ msgstr "Impact élevé sur le prix" msgid "<0>Solana Support<1>Botanix Support<2>GMX Express" msgstr "<0>Support Solana<1>Support Botanix<2>GMX Express" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined take profits are at maximum (100%). Decrease existing values to add more orders." -msgstr "Les take profits combinés sont au maximum (100 %). Diminuez les valeurs existantes pour ajouter plus d'ordres." - #: src/components/GmList/GmList.tsx msgid "POOL" msgstr "POOL" @@ -4580,6 +4640,8 @@ msgstr "Vente en cours" msgid "Recommended" msgstr "Recommandé" +#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Decrease" @@ -4783,11 +4845,12 @@ msgstr "V1 Avalanche" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Collateral ({0})" msgstr "Collatéral ({0})" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This order will not be valid for that position. <0>Switch to {1} collateral" msgstr "Vous avez une position existante avec {0} comme collatéral. Cet ordre ne sera pas valide pour cette position. <0>Passer au collatéral {1}" @@ -4890,10 +4953,6 @@ msgstr "Erreur d'ordre. Les prix sont actuellement volatils pour ce marché, ré msgid "Swap Price Impact for External Swap Threshold" msgstr "Seuil d'impact sur le prix de swap pour les swaps externes" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Limit / Take Profit / Stop Loss" -msgstr "Limite / Take Profit / Stop Loss" - #: landing/src/pages/Home/LiqiuditySection/PoolCards.tsx msgid "Steady returns without management" msgstr "Rendements stables sans gestion" @@ -4906,10 +4965,6 @@ msgstr "Écart" msgid "This position was liquidated as the max. leverage of {formattedMaxLeverage} was exceeded when taking into account fees." msgstr "Cette position a été liquidée car l'effet de levier maximal de {formattedMaxLeverage} a été dépassé en tenant compte des frais." -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Swap {0}" -msgstr "Échanger {0}" - #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx @@ -4999,10 +5054,6 @@ msgstr "APR wstETH" msgid "Telegram Group" msgstr "Groupe Telegram" -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Long TP/SL: {0} a long position when the trigger price is reached." -msgstr "{typeString} Long TP/SL : {0} une position longue lorsque le prix de déclenchement est atteint." - #: src/components/OrderItem/TwapOrdersList/TwapOrdersList.tsx msgid "<0>{fromTokenText} {fromTokenIcon}<1> to {toTokenIcon}" msgstr "<0>{fromTokenText} {fromTokenIcon}<1> vers {toTokenIcon}" @@ -5031,7 +5082,7 @@ msgstr "" #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/GmxAssetCard.tsx #: src/components/MarketStats/MarketGraphs.tsx #: src/components/SwapCard/SwapCard.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TradeboxMarginFields/PriceField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TVChart/Chart.tsx #: src/pages/Dashboard/GmxCard.tsx @@ -5194,6 +5245,10 @@ msgstr "Hash TX du pont" msgid "Start unrealized pnl" msgstr "PnL non réalisé de départ" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of Position)" +msgstr "" + #: src/domain/tokens/approveTokens.tsx msgid "Approval submitted! <0>View status." msgstr "Approbation soumise ! <0>Voir le statut." @@ -5215,7 +5270,7 @@ msgid "Stake GMX to earn GMX rewards" msgstr "Staker du GMX pour gagner des récompenses GMX" #: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionList/PositionList.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "TP/SL" msgstr "TP/SL" @@ -5443,6 +5498,7 @@ msgstr "Gouvernance" #: src/components/OrderItem/OrderItem.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Cancel" msgstr "Annuler" @@ -5509,6 +5565,11 @@ msgstr "90j" msgid "Liquidation" msgstr "Liquidation" +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Add TP/SL" +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Risk Monitoring" msgstr "Surveillance des risques GMX" @@ -5553,6 +5614,7 @@ msgid "Longs Net Rate / 1h" msgstr "Taux nets longs / 1h" #: src/components/OrderItem/OrderItem.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx msgid "Edit" msgstr "Modifier" @@ -5565,10 +5627,6 @@ msgstr "Aucun effet de levier disponible trouvé" msgid "You have a <0>pending transfer to {pendingReceiver}." msgstr "Vous avez un <0>transfert en attente vers {pendingReceiver}." -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Limit size is required" -msgstr "La taille limite est requise" - #: src/components/GmxAccountModal/DepositView.tsx msgid "From Network" msgstr "Depuis le Réseau" @@ -5585,6 +5643,10 @@ msgstr "Retirer du Compte GMX" msgid "Collateral at Liquidation" msgstr "Collatéral à la liquidation" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Keep leverage at {currentLeverage}" +msgstr "" + #: src/lib/dates.ts #: src/pages/AccountDashboard/GeneralPerformanceDetails.tsx msgid "Today" @@ -5969,10 +6031,6 @@ msgstr "Frais de Financement Négatif" msgid "{fromText} to {toExecutionText}" msgstr "{fromText} à {toExecutionText}" -#: src/components/PositionSeller/PositionSeller.tsx -msgid "Create {0} Order" -msgstr "Créer un Ordre {0}" - #: src/context/SubaccountContext/SubaccountContextProvider.tsx msgid "Settings updated." msgstr "Paramètres mis à jour." @@ -6050,11 +6108,6 @@ msgstr "Échanger {fromAmount} pour {toAmount}" msgid "Amount" msgstr "Montant" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create {0} order" -msgstr "Créer un ordre {0}" - #: src/components/PositionEditor/PositionEditor.tsx msgid "Edit {0} {1}{2}" msgstr "Modifier {0} {1}{2}" @@ -6136,6 +6189,7 @@ msgstr "Il pourrait ne pas y avoir suffisamment de liquidité pour exécuter vot #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -6242,6 +6296,7 @@ msgstr "Frais de vente" #: src/components/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Execution Details" msgstr "Détails d'exécution" @@ -6266,7 +6321,7 @@ msgstr "Mettre à jour le Take Profit" msgid "Only addresses with over {0} in capital used are ranked.<0/><1/>The capital used is calculated as the highest value of [<2>sum of collateral of open positions - realized PnL + period start pending PnL]." msgstr "Seules les adresses avec plus de {0} en capital utilisé sont classées.<0/><1/>Le capital utilisé est calculé comme la valeur la plus élevée de [<2>somme du collatéral des positions ouvertes - PnL réalisé + PnL en attente au début de la période]." -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Active Orders" msgstr "Ordres actifs" @@ -6317,6 +6372,10 @@ msgstr "Analyses du protocole" msgid "More active management required compared to GLV" msgstr "Nécessite une gestion plus active par rapport à GLV" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Take Profit Price" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed amount" msgstr "Frais dépassent le montant" @@ -6419,6 +6478,10 @@ msgstr "Agent AI Kudai" msgid "Current Price" msgstr "Prix actuel" +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +msgid "Enter TP/SL price" +msgstr "" + #: src/pages/NftWallet/NftWallet.jsx msgid "Enter NFT ID" msgstr "Entrez l'ID NFT" @@ -6465,10 +6528,6 @@ msgstr "Dépôt" msgid "365d Est. Fees" msgstr "Frais estimés 365j" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below highest limit price" -msgstr "Prix de déclenchement inférieur au prix limite le plus élevé" - #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/ClaimModal.tsx msgid "Select rewards to claim" msgstr "Sélectionner les récompenses à réclamer" @@ -6536,7 +6595,9 @@ msgstr "Réclamer <0>{0}" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx @@ -6581,7 +6642,9 @@ msgid "We Value Your Feedback" msgstr "Nous apprécions votre feedback" #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Stop Loss" msgstr "Stop Loss" @@ -6634,6 +6697,10 @@ msgstr "Erreur lors de la soumission de l'ordre" msgid "Duration" msgstr "Durée" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "TP Price" +msgstr "" + #: src/components/Seo/SEO.tsx msgid "Trade spot or perpetual BTC, ETH, AVAX and other top cryptocurrencies with up to 100x leverage directly from your wallet on Arbitrum and Avalanche." msgstr "Tradez spot ou perpétuel BTC, ETH, AVAX et d'autres cryptos principales avec jusqu'à 100x d'effet de levier directement depuis votre wallet sur Arbitrum et Avalanche." @@ -6790,7 +6857,8 @@ msgstr "Récompenses GMX :" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderItem/OrderItem.tsx #: src/components/OrderList/OrderList.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx msgid "Trigger Price" @@ -7025,6 +7093,10 @@ msgstr "décalage" msgid "Why is the claim in GLV tokens?" msgstr "Pourquoi la réclamation est en jetons GLV ?" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PNL" +msgstr "" + #: src/components/SettingsModal/TradingSettings.tsx msgid "Express + One-Click" msgstr "Express + One-Click" @@ -7091,6 +7163,8 @@ msgstr "Erreur de décalage." #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -7109,7 +7183,7 @@ msgstr "Long" #: src/components/PoolSelector2/PoolSelector2.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx -#: src/components/TradeBox/MarketPoolSelectorRow.tsx +#: src/components/TradeBox/MarketPoolSelectorField.tsx msgid "Pool" msgstr "Pool" @@ -7202,6 +7276,8 @@ msgstr "SNAPSHOT" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Liquidation Price" msgstr "Prix de liquidation" @@ -7259,6 +7335,7 @@ msgstr "Confirmation en cours..." msgid "Deposited:" msgstr "Déposé :" +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Increase" @@ -7384,6 +7461,10 @@ msgstr "Terminé" msgid "Referral Terms" msgstr "Termes de parrainage" +#: src/components/PositionDropdown/PositionDropdown.tsx +msgid "Increase Size (TWAP)" +msgstr "" + #: src/components/Earn/Portfolio/RecommendedAssets/RecommendedAssets.tsx #: src/components/GmList/FeeApyLabel.tsx msgid "Fee APY" @@ -7394,7 +7475,6 @@ msgid "Can I build on top of GMX or integrate it into my DeFi app?" msgstr "Puis-je construire sur GMX ou l'intégrer dans mon app DeFi ?" #: src/components/GmSwap/GmSwapBox/GmSwapWarningsRow.tsx -#: src/components/TradeBox/TradeBox.tsx msgid "Support for GMX accounts on Avalanche will be discontinued soon. Opening new positions from and depositing additional funds to Avalanche GMX accounts is no longer available. We recommend switching to Arbitrum as a settlement network." msgstr "Le support des comptes GMX sur Avalanche sera bientôt interrompu. L'ouverture de nouvelles positions depuis et le dépôt de fonds supplémentaires sur les comptes GMX Avalanche ne sont plus disponibles. Nous recommandons de passer à Arbitrum comme réseau de règlement." @@ -7437,6 +7517,10 @@ msgstr "Seules les lettres, les chiffres et les soulignés sont autorisés." msgid "High TWAP network fee" msgstr "Frais de réseau TWAP élevé" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the TP/SL button to set TP/SL orders." +msgstr "" + #: src/components/Earn/Discovery/EarnProductCard.tsx msgid "What is GM?" msgstr "Qu’est-ce que GM ?" @@ -7562,6 +7646,7 @@ msgstr "<0>toutes les {minutes} minutes{0}" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/EntryPriceRow.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Entry Price" @@ -7636,10 +7721,6 @@ msgstr "Convertir les tokens esGMX en tokens GMX. Veuillez lire les détails du msgid "You have an existing position in the {0} market pool.<0>Switch to {1} market pool" msgstr "Vous avez une position existante dans le pool de marché {0}.<0>Passer au pool de marché {1}" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Stop Loss PnL" -msgstr "PnL Stop Loss" - #: src/components/TVChartContainer/constants.ts msgid "Market - Long Dec." msgstr "Marché - Long Dim." @@ -7787,6 +7868,7 @@ msgstr "Réclamer des fonds" msgid "30d Fee APY" msgstr "APY des frais sur 30 j" +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "Net Price Impact / Fees" msgstr "Impact de Prix Net / Frais" @@ -7858,6 +7940,7 @@ msgstr "Générer un code de parrainage" #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/SwapCard/SwapCard.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "Swap" msgstr "Échanger" @@ -7896,10 +7979,6 @@ msgstr "Langue" msgid "Complete Transfer" msgstr "Finaliser le transfert" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below lowest limit price" -msgstr "Prix de déclenchement inférieur au prix limite le plus bas" - #: src/components/Referrals/TradersStats.tsx msgid "GMX V2 discounts are automatically applied on each trade and are not displayed on this table." msgstr "Les réductions GMX V2 sont automatiquement appliquées sur chaque trade et ne sont pas affichées dans ce tableau." @@ -7976,10 +8055,6 @@ msgstr "Les 30 derniers j" msgid "Dashboards" msgstr "Tableaux de bord" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create TWAP {0} order" -msgstr "Créer un ordre TWAP {0}" - #: src/components/AddressDropdown/AddressDropdownWithoutMultichain.tsx msgid "Address copied to your clipboard." msgstr "Adresse copiée dans votre presse-papiers." @@ -8006,6 +8081,10 @@ msgstr "Ouvrir l'App" msgid "7d" msgstr "7j" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "TP/SL fees" +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "Rage Trade" msgstr "Rage Trade" @@ -8032,6 +8111,7 @@ msgstr "Transfert échoué." #: src/components/OrderList/OrderList.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Mark Price" @@ -8153,7 +8233,7 @@ msgstr "Très probable" msgid "Withdrawing funds from GMX Account..." msgstr "Retrait des fonds du compte GMX…" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing limit order with {symbol} as collateral. <0>Switch to {symbol} collateral" msgstr "Vous avez un ordre limite existant avec {symbol} comme garantie. <0>Passer à la garantie {symbol}" @@ -8272,7 +8352,6 @@ msgstr "Chargement" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/TradeBox/tradeboxConstants.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx #: src/components/TradeHistory/keys.ts #: src/domain/synthetics/positions/utils.ts msgid "Limit" @@ -8324,10 +8403,9 @@ msgid "multiple asset classes" msgstr "plusieurs classes d'actifs" #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/multichain/progress/MultichainTransferProgressView.tsx diff --git a/src/locales/ja/messages.po b/src/locales/ja/messages.po index c9484b8813..0f3a05e0e1 100644 --- a/src/locales/ja/messages.po +++ b/src/locales/ja/messages.po @@ -73,6 +73,11 @@ msgstr "価格" msgid "receive" msgstr "受け取り" +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx +msgid "Create basic TP/SL orders that fully close your position. For advanced TP/SL setup, use the positions list after opening a position." +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Referrals Dashboard" msgstr "GMX紹介ダッシュボード" @@ -133,7 +138,7 @@ msgstr "Avalanche上のイールドオプティマイザー" msgid "Arbitrum" msgstr "Arbitrum" -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Orders ({0})" msgstr "注文 ({0})" @@ -148,6 +153,8 @@ msgstr "ストップロス" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/Referrals/AffiliatesStats.tsx #: src/components/Referrals/TradersStats.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx msgid "Type" @@ -264,6 +271,10 @@ msgstr "マーケットスワップの執行" msgid "Discover" msgstr "発見" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Full Position Close" +msgstr "" + #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "There is no price impact for increase orders, orders are filled at the mark price. <0>Read more." msgstr "増加注文には価格影響がありません。注文はマーク価格で約定されます。<0>詳細はこちら。" @@ -428,7 +439,6 @@ msgstr "最大プールUSDを超過" #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price below mark price" @@ -641,6 +651,10 @@ msgstr "Telegram連絡先(任意)" msgid "Market order will be cancellable in {minutesText}{seconds}s." msgstr "市場注文は{minutesText}{seconds}秒でキャンセル可能になります。" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Edit TP/SL Size" +msgstr "" + #: src/components/UserIncentiveDistribution/AboutGlpIncident.tsx msgid "About GLP Incident" msgstr "GLPインシデントについて" @@ -675,17 +689,19 @@ msgid "Settling" msgstr "決済中" #: src/components/PositionDropdown/PositionDropdown.tsx -#: src/components/PositionItem/PositionItem.tsx msgid "Edit Collateral" msgstr "担保編集" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "SL Price" +msgstr "" + #: src/components/Earn/Discovery/EarnFaq.tsx msgid "GLV and GM both enable liquidity provision on GMX, allowing users to earn fees from three revenue sources that auto-compound into the token's price. GM tokens represent liquidity in a single pool that supports one specific market on GMX. GLV vaults contain multiple GM tokens in an auto-rebalancing composition, thereby aggregating liquidity across various markets that have the same backing tokens (ex: ETH-USDC, or BTC-USDC). GM enables users to provide isolated liquidity with exposure to a specific market, while GLV allows users to passively earn yield from a diverse range of highly utilized pools." msgstr "GLVとGMはどちらもGMX上での流動性提供を可能にし、3つの収益源から得られる手数料をトークン価格に自動複利で還元します。GMトークンはGMX上の特定単一市場を支える単一プールの流動性を表します。GLVボールトは複数のGMトークンを自動リバランス組成で保有し、同じ裏付資産を持つ複数の市場(例:ETH-USDCやBTC-USDC)の流動性を集約します。GMは特定市場に限定した流動性提供を可能にし、GLVは多様な高利用率プールから受動的に利回りを稼ぐことを可能にします。" #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price above mark price" @@ -862,6 +878,10 @@ msgstr "{tokenSymbol}をアンステーク" msgid "MAX" msgstr "最大" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Stop Loss Price" +msgstr "" + #: src/pages/BuyGMX/BuyGMX.tsx msgid "No centralized exchanges available for this network." msgstr "このネットワークに中央集権取引所が利用できません。" @@ -929,6 +949,8 @@ msgstr "GMX Solanaに移動しようとしています。これは別チーム #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx msgid "Pay" msgstr "支払い" @@ -1111,6 +1133,7 @@ msgstr "すべてのトークンを新しいアカウントへ転送すること #: src/components/OrderItem/OrderItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Collateral" msgstr "担保" @@ -1316,6 +1339,10 @@ msgstr "最低注文: {0}" msgid "Wallet is not connected" msgstr "ウォレットが接続されていません" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of position)" +msgstr "" + #: landing/src/pages/Home/HeroSection/AnimatedTitle.tsx msgid "100+ crypto tokens" msgstr "100種類以上の暗号資産" @@ -1338,6 +1365,10 @@ msgstr "GMはGMX V2市場の流動性プロバイダートークンです。V2 msgid "The current price impact is {0}. Consider adding a buffer of 0.30% to it so the order is more likely to be processed." msgstr "現在の価格影響は{0}です。注文が処理されやすくなるよう0.30%のバッファを追加することを検討してください。" +#: src/components/TPSLModal/TPSLModal.tsx +msgid "TP/SL: {positionTitle}" +msgstr "" + #: src/components/SettingsModal/RpcDebugSettings.tsx msgid "Public" msgstr "パブリック" @@ -1444,6 +1475,10 @@ msgstr "タイムスタンプ" msgid "The amount you are trying to withdraw exceeds the limit. Please try an amount smaller than <0>{upperLimitFormatted}." msgstr "引き出そうとしている金額が上限を超えています。<0>{upperLimitFormatted}より小さい金額をお試しください。" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Gain" +msgstr "" + #: src/components/NotifyModal/NotifyModal.tsx msgid "Discover GMX Alerts" msgstr "GMX Alertsを発見" @@ -1668,10 +1703,6 @@ msgstr "スワップパスが見つかりません" msgid "RPnL" msgstr "RPnL" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined stop losses are at maximum (100%). Decrease existing values to add more orders." -msgstr "組み合わせストップロスが最大(100%)です。既存値を減少させて注文を追加してください。" - #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx msgid "4-18 Places" @@ -1768,7 +1799,6 @@ msgid "Min size per part: {0}" msgstr "部分あたり最小サイズ: {0}" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price above liq. price" msgstr "トリガー価格が清算価格を上回っています" @@ -1800,7 +1830,6 @@ msgstr "オプションベースのボールト" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionSeller/PositionSeller.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Mark" @@ -1942,6 +1971,7 @@ msgstr "手数料のUSD価値は獲得時に計算され、インセンティブ #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeboxMarginFields/MarginPercentageSlider.tsx msgid "Max" msgstr "最大" @@ -2020,9 +2050,9 @@ msgstr "{chainName}でGMXを購入" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx +#: src/components/TradeboxMarginFields/SizeField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -2174,6 +2204,7 @@ msgstr "現在のエンドポイント ({0})" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Initial collateral (collateral excluding borrow and funding fee)." msgstr "初期担保(借入および資金手数料を除く担保)。" @@ -2435,10 +2466,6 @@ msgstr "ブリッジとスワップ" msgid "The execution price may vary from your set limit price due to fees and price impact, ensuring you receive the displayed minimum receive amount. <0>Read more." msgstr "約定価格は、手数料と価格影響により、設定した指値価格と異なる場合がありますが、表示された最低受取額は保証されます。<0>詳細はこちら。" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above lowest limit price" -msgstr "トリガー価格が最低リミット価格を上回っています" - #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2528,6 +2555,10 @@ msgstr "APR(実質年利)は毎週水曜日に更新され、その前一週間 msgid "Fees (Incl. Swap)" msgstr "手数料 (スワップを含む)" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "Order update fee" +msgstr "" + #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts msgid "{fromText} to {toMinText}" @@ -2614,6 +2645,7 @@ msgid "Exposure to Backing Tokens" msgstr "バッキングトークンへのエクスポージャー" #: src/components/Referrals/AddAffiliateCode.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Creating..." msgstr "作成中…" @@ -2669,7 +2701,9 @@ msgstr "他のトークンで:" msgid "Returned Collateral" msgstr "返却された担保" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "There are issues in the TP/SL orders." msgstr "TP/SL注文に問題があります。" @@ -2762,10 +2796,6 @@ msgstr "中央集権型取引所からGMXを購入:" msgid "GMX bonds can be bought on Bond Protocol with a discount and a small vesting period:" msgstr "GMXボンドはBond Protocolで割引と短いベスト期間で購入できます:" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above highest limit price" -msgstr "トリガー価格が最高リミット価格を上回っています" - #: src/components/Earn/Discovery/EarnFaq.tsx msgid "Yes, staking and providing liquidity are fully permissionless. The GMX, GLV, and GM tokens are liquid and can be unstaked and withdrawn at any time. For LP tokens, there could be some rare edge cases where your liquidity is being used for active trades. In that case, you can withdraw as soon as positions close." msgstr "はい、ステーキングと流動性提供は完全にパーミッションレスです。GMX、GLV、GMトークンは流動性が高く、いつでもアンステーク・出金可能です。LPトークンについては稀にアクティブ取引で利用中の場合がありますが、ポジションクローズ後すぐに引き出し可能です。" @@ -2895,12 +2925,16 @@ msgstr "<0>{0}{1}の売却<1>{poolName}" msgid "No eligible tokens available on {0} for deposit" msgstr "{0} で入金可能な対象トークンなし" +#: src/components/TPSLModal/TPSLInputRow.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PnL" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Max close amount exceeded" msgstr "最大クローズ額を超えています" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2986,6 +3020,7 @@ msgstr "20秒" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx @@ -3025,7 +3060,8 @@ msgstr "利用可能な流動性が不足しています。流動性があると #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Claims/ClaimsHistory.tsx #: src/components/OrderItem/OrderItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/Referrals/ClaimAffiliatesModal/ClaimAffiliatesModal.tsx #: src/components/TableMarketFilter/MarketFilterBase.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx @@ -3114,7 +3150,6 @@ msgid "Debug values are not available" msgstr "デバッグ値は利用できません" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price below liq. price" msgstr "トリガー価格が清算価格を下回っています" @@ -3247,10 +3282,6 @@ msgstr "平均サイズ" msgid "V1 esGMX" msgstr "V1 esGMX" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Take Profit PnL" -msgstr "テイクプロフィット損益" - #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your GLV or GM tokens, and borrow against them, or put your LP tokens into strategies to maximize your yield." msgstr "GLVまたはGMトークンを貸し出して借り入れを行ったり、LPトークンを戦略に投入して利回りを最大化。" @@ -3295,6 +3326,7 @@ msgstr "請求中..." #: src/components/Earn/AdditionalOpportunities/OpportunityFilters.tsx #: src/components/GmxAccountModal/AvailableToTradeAssetsView.tsx #: src/components/TokenSelector/MultichainMarketTokenSelector.tsx +#: src/components/TPSLModal/TPSLModal.tsx msgid "All" msgstr "すべて" @@ -3321,7 +3353,8 @@ msgstr "{longOrShort}ポジションは1時間あたり{fundingRate}のファン #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/domain/synthetics/positions/utils.ts msgid "TWAP" @@ -3401,10 +3434,6 @@ msgstr "最大スリッページ" msgid "{longOrShort} positions {fundingAction} a funding fee of {fundingRate} per hour and do not pay a borrow fee." msgstr "{longOrShort}ポジションは1時間あたり{fundingRate}のファンディング手数料を{fundingAction}し、借入手数料を支払いません。" -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Short TP/SL: {0} a short position when the trigger price is reached." -msgstr "{typeString} ショートTP/SL: トリガー価格に達したときにショートポジションを{0}。" - #: src/pages/Ecosystem/Ecosystem.tsx msgid "Community-led Telegram groups." msgstr "コミュニティ主導のTelegramグループ" @@ -3438,6 +3467,10 @@ msgstr "マーケットスワップリクエスト" msgid "Insufficient liquidity in the {0} market pool. Select a different pool for this market.{1}" msgstr "{0}マーケットプールの流動性が不足しています。このマーケットで別のプールを選択してください。{1}" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Loss" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "App disabled, pending {0} upgrade" msgstr "{0}アップグレード待ちのためアプリ無効" @@ -3472,6 +3505,10 @@ msgstr "資金手数料の請求に成功" msgid "<0>every {hours} hours{0}" msgstr "<0>毎 {hours}時間{0}" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "{secondLabel}" +msgstr "" + #: src/components/DebugExpressSettings/DebugSwapsSettings.tsx #: src/components/DebugSwapsSettings/DebugSwapsSettings.tsx msgid "Fail External Swaps" @@ -3501,13 +3538,17 @@ msgstr "スワップ申し込み完了。" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/pages/AccountDashboard/DailyAndCumulativePnL.tsx msgid "PnL" msgstr "損益" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "No TP/SL orders" +msgstr "" + #: src/components/TradeBox/ExpressTradingWarningCard.tsx msgid "Express Trading is not available using network's native token {0}. Consider using {1} instead." msgstr "ネットワークのネイティブトークン{0}を使用したエクスプレストレーディングは利用できません。代わりに{1}を使用することを検討してください。" @@ -3773,7 +3814,6 @@ msgstr "獲得" #: src/components/CollateralSelector/CollateralSelector.tsx #: src/components/CollateralSelector/PositionEditorCollateralSelector.tsx -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx msgid "Collateral In" msgstr "担保の種類" @@ -3785,6 +3825,10 @@ msgstr "GMマーケット" msgid "Your opinions and experiences matter to us. Your feedback helps us understand what we are doing well and where we can make enhancements." msgstr "ご意見とご経験は私たちにとって重要です。フィードバックは私たちが何をうまくやっているか、どこを改善できるかを理解するのに役立ちます。" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Create TP/SL" +msgstr "" + #: src/components/GmxAccountModal/DepositView.tsx #: src/components/GmxAccountModal/WithdrawalView.tsx msgid "Estimated Time" @@ -3823,6 +3867,10 @@ msgstr "トレーダーとしてこのアカウントが獲得したリベート msgid "Transfer account" msgstr "アカウント転送" +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Cancel all" +msgstr "" + #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your tokens, or borrow against it." @@ -3845,8 +3893,6 @@ msgstr "<0>GMXは伝統的な注文ブックとは異なり、動的にバラン #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx msgid "Read more" msgstr "詳細について読む" @@ -3915,6 +3961,7 @@ msgstr "さらに探す" #: src/components/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/AllowedSwapSlippageInputRowImpl/AllowedSwapSlippageInputRowImpl.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/pages/LeaderboardPage/components/LeaderboardAccountsTable.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -3941,6 +3988,8 @@ msgstr "注文のトリガー価格" #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -4009,10 +4058,6 @@ msgstr "トレーディングペア価格アラート" msgid "Links" msgstr "リンク" -#: src/components/PositionItem/PositionItem.tsx -msgid "Use the \"Close\" button to reduce your position via market, TP/SL, or TWAP orders." -msgstr "「クローズ」ボタンを使用して、マーケット、TP/SL、またはTWAP注文でポジションを縮小してください。" - #: src/components/Seo/SEO.tsx msgid "GMX | Decentralized Perpetual Exchange" msgstr "GMX | 分散型永続取引所" @@ -4227,7 +4272,7 @@ msgstr "求人" msgid "Decentralized Options Strategies" msgstr "分散型オプションストラテジー" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This action will not apply for that position. <0>Switch to {1} collateral" msgstr "既存のポジションに{0}を担保として持っています。このアクションはそのポジションに適用されません。<0>{1}担保に切り替え" @@ -4267,7 +4312,9 @@ msgid "Amount of traders you referred." msgstr "あなたが紹介したトレーダー数" #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Take Profit" msgstr "テイクプロフィット" @@ -4285,6 +4332,14 @@ msgstr "{typeString} ロングストップマーケット:価格がトリガ msgid "Insufficient {0} liquidity" msgstr "{0}流動性が不足しています" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Unable to calculate order" +msgstr "" + +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "TP/SL: {positionTitle} Decrease" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.tsx #: src/pages/Ecosystem/Ecosystem.tsx msgid "Creator" @@ -4310,6 +4365,10 @@ msgstr "リミットを作成" msgid "Transfer already initiated" msgstr "転送は既に開始されています" +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +msgid "Margin to Pay" +msgstr "" + #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx msgid "Withdraw submitted." msgstr "出金申し込み完了。" @@ -4387,7 +4446,8 @@ msgstr "Botanix" msgid "Accrued price impact rebates. They will become claimable after 5 days.<0/><1/><2>Read more." msgstr "蓄積された価格影響リベート。5日後に請求可能になります。<0/><1/><2>詳細を読む。" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "Take Profit / Stop Loss" msgstr "テイクプロフィット / ストップロス" @@ -4483,6 +4543,10 @@ msgstr "マーケットが見つかりません。" msgid "RPC Monitoring" msgstr "RPCモニタリング" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the \"Close\" button to reduce your position via market or TWAP orders." +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "ODOS" msgstr "ODOS" @@ -4544,10 +4608,6 @@ msgstr "高い価格影響" msgid "<0>Solana Support<1>Botanix Support<2>GMX Express" msgstr "<0>Solanaサポート<1>Botanixサポート<2>GMXエクスプレス" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined take profits are at maximum (100%). Decrease existing values to add more orders." -msgstr "組み合わせテイクプロフィットが最大(100%)です。既存の値を減少させて注文を追加してください。" - #: src/components/GmList/GmList.tsx msgid "POOL" msgstr "プール" @@ -4580,6 +4640,8 @@ msgstr "売却中" msgid "Recommended" msgstr "おすすめ" +#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Decrease" @@ -4783,11 +4845,12 @@ msgstr "V1 Avalanche" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Collateral ({0})" msgstr "担保 ({0})" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This order will not be valid for that position. <0>Switch to {1} collateral" msgstr "{0}を担保とする既存のポジションがあります。この注文はそのポジションには有効ではありません。<0>{1}担保に切り替える" @@ -4890,10 +4953,6 @@ msgstr "注文エラー。この市場の価格が現在変動しています。 msgid "Swap Price Impact for External Swap Threshold" msgstr "外部スワップ閾値のスワップ価格インパクト" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Limit / Take Profit / Stop Loss" -msgstr "指値 / テイクプロフィット / ストップロス" - #: landing/src/pages/Home/LiqiuditySection/PoolCards.tsx msgid "Steady returns without management" msgstr "管理なしの安定リターン" @@ -4906,10 +4965,6 @@ msgstr "スプレッド" msgid "This position was liquidated as the max. leverage of {formattedMaxLeverage} was exceeded when taking into account fees." msgstr "このポジションは手数料を考慮した上での最大レバレッジ倍率({formattedMaxLeverage})を超過したため清算されました。" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Swap {0}" -msgstr "{0}をスワップ" - #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx @@ -4999,10 +5054,6 @@ msgstr "wstETH APR" msgid "Telegram Group" msgstr "Telegramグループ" -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Long TP/SL: {0} a long position when the trigger price is reached." -msgstr "{typeString} ロング TP/SL: トリガー価格に達したときに {0} のロングポジション。" - #: src/components/OrderItem/TwapOrdersList/TwapOrdersList.tsx msgid "<0>{fromTokenText} {fromTokenIcon}<1> to {toTokenIcon}" msgstr "<0>{fromTokenText} {fromTokenIcon}<1> から {toTokenIcon}" @@ -5031,7 +5082,7 @@ msgstr "" #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/GmxAssetCard.tsx #: src/components/MarketStats/MarketGraphs.tsx #: src/components/SwapCard/SwapCard.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TradeboxMarginFields/PriceField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TVChart/Chart.tsx #: src/pages/Dashboard/GmxCard.tsx @@ -5194,6 +5245,10 @@ msgstr "ブリッジTXハッシュ" msgid "Start unrealized pnl" msgstr "未実現PnL開始" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of Position)" +msgstr "" + #: src/domain/tokens/approveTokens.tsx msgid "Approval submitted! <0>View status." msgstr "承認が提出されました! <0>状況を確認する" @@ -5215,7 +5270,7 @@ msgid "Stake GMX to earn GMX rewards" msgstr "GMXをステークしてGMX報酬を獲得" #: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionList/PositionList.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "TP/SL" msgstr "TP/SL" @@ -5443,6 +5498,7 @@ msgstr "ガバナンス" #: src/components/OrderItem/OrderItem.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Cancel" msgstr "キャンセル" @@ -5509,6 +5565,11 @@ msgstr "90日" msgid "Liquidation" msgstr "清算" +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Add TP/SL" +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Risk Monitoring" msgstr "GMXリスク監視" @@ -5553,6 +5614,7 @@ msgid "Longs Net Rate / 1h" msgstr "ロングネットレート / 1時間" #: src/components/OrderItem/OrderItem.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx msgid "Edit" msgstr "編集" @@ -5565,10 +5627,6 @@ msgstr "利用可能なレバレッジが見つかりません" msgid "You have a <0>pending transfer to {pendingReceiver}." msgstr "<0>保留中の転送が{pendingReceiver}宛てにあります。" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Limit size is required" -msgstr "指値サイズが必要です" - #: src/components/GmxAccountModal/DepositView.tsx msgid "From Network" msgstr "送信元ネットワーク" @@ -5585,6 +5643,10 @@ msgstr "GMXアカウントから引き出し" msgid "Collateral at Liquidation" msgstr "清算時の担保" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Keep leverage at {currentLeverage}" +msgstr "" + #: src/lib/dates.ts #: src/pages/AccountDashboard/GeneralPerformanceDetails.tsx msgid "Today" @@ -5969,10 +6031,6 @@ msgstr "ネガティブ資金手数料" msgid "{fromText} to {toExecutionText}" msgstr "{fromText}から{toExecutionText}へ" -#: src/components/PositionSeller/PositionSeller.tsx -msgid "Create {0} Order" -msgstr "{0}注文を作成" - #: src/context/SubaccountContext/SubaccountContextProvider.tsx msgid "Settings updated." msgstr "設定が更新されました。" @@ -6050,11 +6108,6 @@ msgstr "{fromAmount} を {toAmount} にスワップ" msgid "Amount" msgstr "額" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create {0} order" -msgstr "{0}の注文を作成" - #: src/components/PositionEditor/PositionEditor.tsx msgid "Edit {0} {1}{2}" msgstr "{0} {1}{2} を編集" @@ -6136,6 +6189,7 @@ msgstr "最小受取が満たされたときに注文を実行するための流 #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -6242,6 +6296,7 @@ msgstr "売却手数料" #: src/components/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Execution Details" msgstr "執行詳細" @@ -6266,7 +6321,7 @@ msgstr "テイクプロフィットを更新" msgid "Only addresses with over {0} in capital used are ranked.<0/><1/>The capital used is calculated as the highest value of [<2>sum of collateral of open positions - realized PnL + period start pending PnL]." msgstr "{0}以上の使用資本を持つアドレスのみがランキングされます。<0/><1/>使用資本は、[<2>オープンポジションの担保合計 - 実現PnL + 期間開始時の未決済PnL]の最高値として計算されます。" -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Active Orders" msgstr "アクティブな注文" @@ -6317,6 +6372,10 @@ msgstr "プロトコルの分析" msgid "More active management required compared to GLV" msgstr "GLVに比べてアクティブ運用が必要" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Take Profit Price" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed amount" msgstr "手数料が額を超過" @@ -6419,6 +6478,10 @@ msgstr "Kudai AI Agent" msgid "Current Price" msgstr "現在価格" +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +msgid "Enter TP/SL price" +msgstr "" + #: src/pages/NftWallet/NftWallet.jsx msgid "Enter NFT ID" msgstr "NFT IDを入力" @@ -6465,10 +6528,6 @@ msgstr "入金" msgid "365d Est. Fees" msgstr "365日推定手数料" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below highest limit price" -msgstr "トリガー価格が最高指値価格を下回る" - #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/ClaimModal.tsx msgid "Select rewards to claim" msgstr "請求する報酬を選択" @@ -6536,7 +6595,9 @@ msgstr "<0>{0} を請求" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx @@ -6581,7 +6642,9 @@ msgid "We Value Your Feedback" msgstr "あなたのフィードバックを大切にします" #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Stop Loss" msgstr "ストップロス" @@ -6634,6 +6697,10 @@ msgstr "注文提出エラー" msgid "Duration" msgstr "期間" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "TP Price" +msgstr "" + #: src/components/Seo/SEO.tsx msgid "Trade spot or perpetual BTC, ETH, AVAX and other top cryptocurrencies with up to 100x leverage directly from your wallet on Arbitrum and Avalanche." msgstr "ArbitrumおよびAvalanche上でウォレットから直接、最大100xレバレッジでBTC、ETH、AVAXおよび他のトップ暗号通貨のスポットまたはパーペチュアルを取引。" @@ -6790,7 +6857,8 @@ msgstr "GMX報酬:" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderItem/OrderItem.tsx #: src/components/OrderList/OrderList.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx msgid "Trigger Price" @@ -7025,6 +7093,10 @@ msgstr "シフト" msgid "Why is the claim in GLV tokens?" msgstr "なぜGLVトークンで請求なのですか?" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PNL" +msgstr "" + #: src/components/SettingsModal/TradingSettings.tsx msgid "Express + One-Click" msgstr "エクスプレス + ワンクリック" @@ -7091,6 +7163,8 @@ msgstr "シフトエラー。" #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -7109,7 +7183,7 @@ msgstr "ロング" #: src/components/PoolSelector2/PoolSelector2.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx -#: src/components/TradeBox/MarketPoolSelectorRow.tsx +#: src/components/TradeBox/MarketPoolSelectorField.tsx msgid "Pool" msgstr "プール" @@ -7202,6 +7276,8 @@ msgstr "スナップショット" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Liquidation Price" msgstr "清算価格" @@ -7259,6 +7335,7 @@ msgstr "確認中..." msgid "Deposited:" msgstr "入金済み:" +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Increase" @@ -7384,6 +7461,10 @@ msgstr "完了" msgid "Referral Terms" msgstr "紹介規約" +#: src/components/PositionDropdown/PositionDropdown.tsx +msgid "Increase Size (TWAP)" +msgstr "" + #: src/components/Earn/Portfolio/RecommendedAssets/RecommendedAssets.tsx #: src/components/GmList/FeeApyLabel.tsx msgid "Fee APY" @@ -7394,7 +7475,6 @@ msgid "Can I build on top of GMX or integrate it into my DeFi app?" msgstr "GMXの上に構築したりDeFiアプリに統合できますか?" #: src/components/GmSwap/GmSwapBox/GmSwapWarningsRow.tsx -#: src/components/TradeBox/TradeBox.tsx msgid "Support for GMX accounts on Avalanche will be discontinued soon. Opening new positions from and depositing additional funds to Avalanche GMX accounts is no longer available. We recommend switching to Arbitrum as a settlement network." msgstr "Avalanche上のGMXアカウントのサポートはまもなく終了します。Avalanche GMXアカウントからの新規ポジションの開設および追加資金の入金は利用できなくなりました。決済ネットワークとしてArbitrumへの切り替えをお勧めします。" @@ -7437,6 +7517,10 @@ msgstr "文字、数字、アンダースコアのみ許可されます。" msgid "High TWAP network fee" msgstr "高いTWAPネットワーク手数料" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the TP/SL button to set TP/SL orders." +msgstr "" + #: src/components/Earn/Discovery/EarnProductCard.tsx msgid "What is GM?" msgstr "GMとは?" @@ -7562,6 +7646,7 @@ msgstr "<0>毎 {minutes} 分{0}" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/EntryPriceRow.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Entry Price" @@ -7636,10 +7721,6 @@ msgstr "esGMXトークンをGMXトークンに変換します。ボールト使 msgid "You have an existing position in the {0} market pool.<0>Switch to {1} market pool" msgstr "{0}市場プールに既存のポジションがあります。<0>{1}市場プールに切り替え" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Stop Loss PnL" -msgstr "ストップロスPnL" - #: src/components/TVChartContainer/constants.ts msgid "Market - Long Dec." msgstr "市場 - ロング減少" @@ -7787,6 +7868,7 @@ msgstr "資金を請求" msgid "30d Fee APY" msgstr "30日手数料APY" +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "Net Price Impact / Fees" msgstr "ネット価格影響 / 手数料" @@ -7858,6 +7940,7 @@ msgstr "紹介コード生成" #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/SwapCard/SwapCard.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "Swap" msgstr "スワップ" @@ -7896,10 +7979,6 @@ msgstr "言語" msgid "Complete Transfer" msgstr "転送を完了" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below lowest limit price" -msgstr "トリガー価格が最低リミット価格を下回っています" - #: src/components/Referrals/TradersStats.tsx msgid "GMX V2 discounts are automatically applied on each trade and are not displayed on this table." msgstr "GMX V2割引は各取引で自動適用され、このテーブルに表示されません。" @@ -7976,10 +8055,6 @@ msgstr "過去30日" msgid "Dashboards" msgstr "ダッシュボード" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create TWAP {0} order" -msgstr "TWAP {0}注文を作成" - #: src/components/AddressDropdown/AddressDropdownWithoutMultichain.tsx msgid "Address copied to your clipboard." msgstr "アドレスがクリップボードにコピーされました。" @@ -8006,6 +8081,10 @@ msgstr "アプリを開く" msgid "7d" msgstr "7日" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "TP/SL fees" +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "Rage Trade" msgstr "Rage Trade" @@ -8032,6 +8111,7 @@ msgstr "転送失敗。" #: src/components/OrderList/OrderList.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Mark Price" @@ -8153,7 +8233,7 @@ msgstr "非常に可能性が高い" msgid "Withdrawing funds from GMX Account..." msgstr "GMXアカウントから資金を出金中…" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing limit order with {symbol} as collateral. <0>Switch to {symbol} collateral" msgstr "{symbol}を担保とした既存の制限注文があります。<0>{symbol}担保に切り替え" @@ -8272,7 +8352,6 @@ msgstr "読み込み中" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/TradeBox/tradeboxConstants.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx #: src/components/TradeHistory/keys.ts #: src/domain/synthetics/positions/utils.ts msgid "Limit" @@ -8324,10 +8403,9 @@ msgid "multiple asset classes" msgstr "複数の資産クラス" #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/multichain/progress/MultichainTransferProgressView.tsx diff --git a/src/locales/ko/messages.po b/src/locales/ko/messages.po index d5a18c2ec6..befcbe93de 100644 --- a/src/locales/ko/messages.po +++ b/src/locales/ko/messages.po @@ -73,6 +73,11 @@ msgstr "가격" msgid "receive" msgstr "수령" +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx +msgid "Create basic TP/SL orders that fully close your position. For advanced TP/SL setup, use the positions list after opening a position." +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Referrals Dashboard" msgstr "GMX 추천인 대시보드" @@ -133,7 +138,7 @@ msgstr "Avalanche 일드 옵티마이저" msgid "Arbitrum" msgstr "Arbitrum" -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Orders ({0})" msgstr "주문 ({0})" @@ -148,6 +153,8 @@ msgstr "손절" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/Referrals/AffiliatesStats.tsx #: src/components/Referrals/TradersStats.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx msgid "Type" @@ -264,6 +271,10 @@ msgstr "마켓 스왑 실행" msgid "Discover" msgstr "탐색" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Full Position Close" +msgstr "" + #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "There is no price impact for increase orders, orders are filled at the mark price. <0>Read more." msgstr "증가 주문에는 가격 영향이 없습니다. 주문은 마크 가격에 체결됩니다. <0>자세히 보기." @@ -428,7 +439,6 @@ msgstr "최대 풀 USD 초과" #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price below mark price" @@ -641,6 +651,10 @@ msgstr "텔레그램 연락처 (선택)" msgid "Market order will be cancellable in {minutesText}{seconds}s." msgstr "마켓 주문은 {minutesText}{seconds}초 후 취소 가능." +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Edit TP/SL Size" +msgstr "" + #: src/components/UserIncentiveDistribution/AboutGlpIncident.tsx msgid "About GLP Incident" msgstr "GLP 사건에 대해" @@ -675,17 +689,19 @@ msgid "Settling" msgstr "정산 중" #: src/components/PositionDropdown/PositionDropdown.tsx -#: src/components/PositionItem/PositionItem.tsx msgid "Edit Collateral" msgstr "담보 수정" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "SL Price" +msgstr "" + #: src/components/Earn/Discovery/EarnFaq.tsx msgid "GLV and GM both enable liquidity provision on GMX, allowing users to earn fees from three revenue sources that auto-compound into the token's price. GM tokens represent liquidity in a single pool that supports one specific market on GMX. GLV vaults contain multiple GM tokens in an auto-rebalancing composition, thereby aggregating liquidity across various markets that have the same backing tokens (ex: ETH-USDC, or BTC-USDC). GM enables users to provide isolated liquidity with exposure to a specific market, while GLV allows users to passively earn yield from a diverse range of highly utilized pools." msgstr "GLV와 GM은 모두 GMX에서 유동성 제공을 가능하게 하며, 세 가지 수익원에서 발생하는 수수료를 토큰 가격에 자동 복리로 반영하여 수익을 제공합니다. GM 토큰은 GMX의 특정 단일 시장을 지원하는 풀의 유동성을 나타냅니다. GLV 볼트는 여러 GM 토큰을 자동 리밸런싱 구성으로 보유하여 동일한 담보 토큰을 사용하는 여러 시장(예: ETH-USDC, BTC-USDC)의 유동성을 집합합니다. GM은 특정 시장에 노출된 격리된 유동성 제공을 가능하게 하고, GLV는 다양한 고활용 풀에서 수동적으로 수익을 얻을 수 있게 합니다." #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price above mark price" @@ -862,6 +878,10 @@ msgstr "{tokenSymbol} 언스테이킹" msgid "MAX" msgstr "최대" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Stop Loss Price" +msgstr "" + #: src/pages/BuyGMX/BuyGMX.tsx msgid "No centralized exchanges available for this network." msgstr "이 네트워크에 중앙화 거래소가 없습니다." @@ -929,6 +949,8 @@ msgstr "GMX Solana로 이동하려고 합니다. 별도의 팀이 운영하므 #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx msgid "Pay" msgstr "지불" @@ -1111,6 +1133,7 @@ msgstr "모든 토큰을 새 계정으로 전송 허용" #: src/components/OrderItem/OrderItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Collateral" msgstr "담보" @@ -1316,6 +1339,10 @@ msgstr "최소 주문: {0}" msgid "Wallet is not connected" msgstr "지갑이 연결되지 않았습니다" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of position)" +msgstr "" + #: landing/src/pages/Home/HeroSection/AnimatedTitle.tsx msgid "100+ crypto tokens" msgstr "100개 이상의 암호화폐 토큰" @@ -1338,6 +1365,10 @@ msgstr "GM은 GMX V2 마켓의 유동성 제공자 토큰입니다. V2 마켓 msgid "The current price impact is {0}. Consider adding a buffer of 0.30% to it so the order is more likely to be processed." msgstr "현재 가격 영향은 {0}입니다. 주문이 처리될 가능성을 높이기 위해 0.30% 버퍼를 추가하는 것을 고려하세요." +#: src/components/TPSLModal/TPSLModal.tsx +msgid "TP/SL: {positionTitle}" +msgstr "" + #: src/components/SettingsModal/RpcDebugSettings.tsx msgid "Public" msgstr "공개" @@ -1444,6 +1475,10 @@ msgstr "타임스탬프" msgid "The amount you are trying to withdraw exceeds the limit. Please try an amount smaller than <0>{upperLimitFormatted}." msgstr "출금하려는 금액이 한도를 초과합니다. <0>{upperLimitFormatted}보다 작은 금액을 시도하세요." +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Gain" +msgstr "" + #: src/components/NotifyModal/NotifyModal.tsx msgid "Discover GMX Alerts" msgstr "GMX 알림 발견" @@ -1668,10 +1703,6 @@ msgstr "스왑 경로 없음" msgid "RPnL" msgstr "RPnL" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined stop losses are at maximum (100%). Decrease existing values to add more orders." -msgstr "결합 손절이 최대(100%)입니다. 더 많은 주문을 추가하려면 기존 값을 감소시키세요." - #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx msgid "4-18 Places" @@ -1768,7 +1799,6 @@ msgid "Min size per part: {0}" msgstr "부분당 최소 크기: {0}" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price above liq. price" msgstr "트리거 가격이 청산 가격 초과" @@ -1800,7 +1830,6 @@ msgstr "옵션 기반 볼트" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionSeller/PositionSeller.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Mark" @@ -1942,6 +1971,7 @@ msgstr "수수료의 USD 가치는 적립 시 계산되며 인센티브를 포 #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeboxMarginFields/MarginPercentageSlider.tsx msgid "Max" msgstr "최대" @@ -2020,9 +2050,9 @@ msgstr "{chainName}에서 GMX 구매" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx +#: src/components/TradeboxMarginFields/SizeField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -2174,6 +2204,7 @@ msgstr "현재 엔드포인트 ({0})" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Initial collateral (collateral excluding borrow and funding fee)." msgstr "초기 담보(대출 및 펀딩 수수료 제외 담보)." @@ -2435,10 +2466,6 @@ msgstr "브릿지 및 스왑" msgid "The execution price may vary from your set limit price due to fees and price impact, ensuring you receive the displayed minimum receive amount. <0>Read more." msgstr "체결 가격은 수수료와 가격 영향으로 인해 설정한 지정가와 다를 수 있지만, 표시된 최소 수령 금액은 보장됩니다. <0>자세히 보기." -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above lowest limit price" -msgstr "트리거 가격이 최저 지정가 가격 초과" - #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2528,6 +2555,10 @@ msgstr "APR은 매주 수요일에 업데이트되며 한 주 동안 쌓인 수 msgid "Fees (Incl. Swap)" msgstr "수수료 (스왑 포함)" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "Order update fee" +msgstr "" + #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts msgid "{fromText} to {toMinText}" @@ -2614,6 +2645,7 @@ msgid "Exposure to Backing Tokens" msgstr "기반 토큰 노출" #: src/components/Referrals/AddAffiliateCode.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Creating..." msgstr "생성 중…" @@ -2669,7 +2701,9 @@ msgstr "다른 토큰으로:" msgid "Returned Collateral" msgstr "반환된 담보" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "There are issues in the TP/SL orders." msgstr "TP/SL 주문에 문제가 있습니다." @@ -2762,10 +2796,6 @@ msgstr "중앙화 거래소에서 GMX 구매:" msgid "GMX bonds can be bought on Bond Protocol with a discount and a small vesting period:" msgstr "GMX 채권은 Bond Protocol에서 할인과 짧은 베스팅 기간으로 구매할 수 있습니다:" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above highest limit price" -msgstr "트리거 가격이 최고 지정가 가격 초과" - #: src/components/Earn/Discovery/EarnFaq.tsx msgid "Yes, staking and providing liquidity are fully permissionless. The GMX, GLV, and GM tokens are liquid and can be unstaked and withdrawn at any time. For LP tokens, there could be some rare edge cases where your liquidity is being used for active trades. In that case, you can withdraw as soon as positions close." msgstr "네, 스테이킹과 유동성 제공은 완전히 허가 없이 가능합니다. GMX, GLV, GM 토큰은 유동성이 있으며 언제든지 언스테이킹 및 출금이 가능합니다. LP 토큰의 경우 매우 드물게 유동성이 활성 거래에 사용 중인 경우가 있을 수 있으며, 이 경우 포지션이 청산되는 즉시 출금할 수 있습니다." @@ -2895,12 +2925,16 @@ msgstr "<0>{0}{1} 판매<1>{poolName}" msgid "No eligible tokens available on {0} for deposit" msgstr "{0}에서 입금 가능한 적격 토큰 없음" +#: src/components/TPSLModal/TPSLInputRow.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PnL" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Max close amount exceeded" msgstr "최대 닫기 금액 초과" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2986,6 +3020,7 @@ msgstr "20초" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx @@ -3025,7 +3060,8 @@ msgstr "사용 가능한 유동성이 부족합니다. 유동성이 있을 때 #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Claims/ClaimsHistory.tsx #: src/components/OrderItem/OrderItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/Referrals/ClaimAffiliatesModal/ClaimAffiliatesModal.tsx #: src/components/TableMarketFilter/MarketFilterBase.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx @@ -3114,7 +3150,6 @@ msgid "Debug values are not available" msgstr "디버그 값 사용 불가" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price below liq. price" msgstr "트리거 가격이 청산 가격 미만" @@ -3247,10 +3282,6 @@ msgstr "평균 크기" msgid "V1 esGMX" msgstr "V1 esGMX" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Take Profit PnL" -msgstr "이익 실현 PnL" - #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your GLV or GM tokens, and borrow against them, or put your LP tokens into strategies to maximize your yield." msgstr "GLV 또는 GM 토큰을 대출하거나 담보로 대출받고, LP 토큰을 전략에 투입하여 수익을 극대화하세요." @@ -3295,6 +3326,7 @@ msgstr "수령중..." #: src/components/Earn/AdditionalOpportunities/OpportunityFilters.tsx #: src/components/GmxAccountModal/AvailableToTradeAssetsView.tsx #: src/components/TokenSelector/MultichainMarketTokenSelector.tsx +#: src/components/TPSLModal/TPSLModal.tsx msgid "All" msgstr "모두" @@ -3321,7 +3353,8 @@ msgstr "{longOrShort} 포지션은 시간당 {fundingRate} 자금 수수료를 { #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/domain/synthetics/positions/utils.ts msgid "TWAP" @@ -3401,10 +3434,6 @@ msgstr "허용 가능한 슬리피지" msgid "{longOrShort} positions {fundingAction} a funding fee of {fundingRate} per hour and do not pay a borrow fee." msgstr "{longOrShort} 포지션은 시간당 {fundingRate} 자금 수수료를 {fundingAction}하며 차입 수수료를 지불하지 않습니다." -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Short TP/SL: {0} a short position when the trigger price is reached." -msgstr "{typeString} 숏 TP/SL: 트리거 가격에 도달할 때 숏 포지션 {0}." - #: src/pages/Ecosystem/Ecosystem.tsx msgid "Community-led Telegram groups." msgstr "커뮤니티 주도 텔레그램 그룹" @@ -3438,6 +3467,10 @@ msgstr "마켓 스왑 요청" msgid "Insufficient liquidity in the {0} market pool. Select a different pool for this market.{1}" msgstr "{0} 시장 풀의 유동성 부족. 이 시장에 대해 다른 풀을 선택하세요.{1}" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Loss" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "App disabled, pending {0} upgrade" msgstr "{0} 업그레이드 대기 중 앱 비활성화" @@ -3472,6 +3505,10 @@ msgstr "펀딩 수수료 청구 성공" msgid "<0>every {hours} hours{0}" msgstr "<0>매 {hours} 시간{0}" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "{secondLabel}" +msgstr "" + #: src/components/DebugExpressSettings/DebugSwapsSettings.tsx #: src/components/DebugSwapsSettings/DebugSwapsSettings.tsx msgid "Fail External Swaps" @@ -3501,13 +3538,17 @@ msgstr "스왑 제출 완료." #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/pages/AccountDashboard/DailyAndCumulativePnL.tsx msgid "PnL" msgstr "PnL" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "No TP/SL orders" +msgstr "" + #: src/components/TradeBox/ExpressTradingWarningCard.tsx msgid "Express Trading is not available using network's native token {0}. Consider using {1} instead." msgstr "네트워크의 네이티브 토큰 {0}을 사용한 익스프레스 거래는 사용할 수 없습니다. 대신 {1} 사용을 고려하세요." @@ -3773,7 +3814,6 @@ msgstr "수익" #: src/components/CollateralSelector/CollateralSelector.tsx #: src/components/CollateralSelector/PositionEditorCollateralSelector.tsx -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx msgid "Collateral In" msgstr "담보 자산" @@ -3785,6 +3825,10 @@ msgstr "GM 시장" msgid "Your opinions and experiences matter to us. Your feedback helps us understand what we are doing well and where we can make enhancements." msgstr "귀하의 의견과 경험은 우리에게 중요합니다. 피드백은 우리가 잘하고 있는 부분과 개선할 부분을 이해하는 데 도움이 됩니다." +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Create TP/SL" +msgstr "" + #: src/components/GmxAccountModal/DepositView.tsx #: src/components/GmxAccountModal/WithdrawalView.tsx msgid "Estimated Time" @@ -3823,6 +3867,10 @@ msgstr "트레이더로서 이 계정이 획득한 소개 보수." msgid "Transfer account" msgstr "계정 전송" +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Cancel all" +msgstr "" + #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your tokens, or borrow against it." @@ -3845,8 +3893,6 @@ msgstr "<0>GMX는 전통적인 주문장과 달리 동적으로 균형 잡힌 #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx msgid "Read more" msgstr "더 알아보기" @@ -3915,6 +3961,7 @@ msgstr "더 탐색하기" #: src/components/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/AllowedSwapSlippageInputRowImpl/AllowedSwapSlippageInputRowImpl.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/pages/LeaderboardPage/components/LeaderboardAccountsTable.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -3941,6 +3988,8 @@ msgstr "주문의 트리거 가격입니다." #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -4009,10 +4058,6 @@ msgstr "거래 쌍 가격 알림" msgid "Links" msgstr "링크" -#: src/components/PositionItem/PositionItem.tsx -msgid "Use the \"Close\" button to reduce your position via market, TP/SL, or TWAP orders." -msgstr "\"닫기\" 버튼을 사용하여 시장가, TP/SL 또는 TWAP 주문으로 포지션을 줄이세요." - #: src/components/Seo/SEO.tsx msgid "GMX | Decentralized Perpetual Exchange" msgstr "GMX | 탈중앙화 퍼페추얼 거래소" @@ -4227,7 +4272,7 @@ msgstr "구인" msgid "Decentralized Options Strategies" msgstr "탈중앙화 옵션 전략" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This action will not apply for that position. <0>Switch to {1} collateral" msgstr "{0}을 담보로 한 기존 포지션이 있습니다. 이 작업은 해당 포지션에 적용되지 않습니다. <0>{1} 담보로 전환" @@ -4267,7 +4312,9 @@ msgid "Amount of traders you referred." msgstr "추천한 거래자 수" #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Take Profit" msgstr "이익 실현" @@ -4285,6 +4332,14 @@ msgstr "{typeString} 롱 스탑 마켓: 가격이 트리거 가격 아래일 때 msgid "Insufficient {0} liquidity" msgstr "{0} 유동성 부족" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Unable to calculate order" +msgstr "" + +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "TP/SL: {positionTitle} Decrease" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.tsx #: src/pages/Ecosystem/Ecosystem.tsx msgid "Creator" @@ -4310,6 +4365,10 @@ msgstr "지정가 생성" msgid "Transfer already initiated" msgstr "이미 전송이 시작되었습니다" +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +msgid "Margin to Pay" +msgstr "" + #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx msgid "Withdraw submitted." msgstr "인출 제출 완료." @@ -4387,7 +4446,8 @@ msgstr "Botanix" msgid "Accrued price impact rebates. They will become claimable after 5 days.<0/><1/><2>Read more." msgstr "축적된 가격 영향 리베이트. 5일 후 청구 가능합니다.<0/><1/><2>더 읽기." -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "Take Profit / Stop Loss" msgstr "이익 실현 / 손절" @@ -4483,6 +4543,10 @@ msgstr "시장 없음." msgid "RPC Monitoring" msgstr "RPC 모니터링" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the \"Close\" button to reduce your position via market or TWAP orders." +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "ODOS" msgstr "ODOS" @@ -4544,10 +4608,6 @@ msgstr "높은 가격 영향" msgid "<0>Solana Support<1>Botanix Support<2>GMX Express" msgstr "<0>솔라나 지원<1>Botanix 지원<2>GMX 익스프레스" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined take profits are at maximum (100%). Decrease existing values to add more orders." -msgstr "결합된 이익 실현이 최대(100%)입니다. 기존 값을 줄여 더 많은 주문을 추가하세요." - #: src/components/GmList/GmList.tsx msgid "POOL" msgstr "풀" @@ -4580,6 +4640,8 @@ msgstr "판매 중" msgid "Recommended" msgstr "추천" +#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Decrease" @@ -4783,11 +4845,12 @@ msgstr "V1 Avalanche" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Collateral ({0})" msgstr "담보 ({0})" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This order will not be valid for that position. <0>Switch to {1} collateral" msgstr "{0}을(를) 담보로 하는 기존 포지션이 있습니다. 이 주문은 해당 포지션에 유효하지 않습니다. <0>{1} 담보로 전환" @@ -4890,10 +4953,6 @@ msgstr "주문 오류. 이 시장의 가격이 현재 변동성이 큽니다. msgid "Swap Price Impact for External Swap Threshold" msgstr "외부 스왑 임계값에 대한 스왑 가격 영향" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Limit / Take Profit / Stop Loss" -msgstr "지정가 / 이익 실현 / 손절" - #: landing/src/pages/Home/LiqiuditySection/PoolCards.tsx msgid "Steady returns without management" msgstr "관리 없이 안정적인 수익" @@ -4906,10 +4965,6 @@ msgstr "스프레드" msgid "This position was liquidated as the max. leverage of {formattedMaxLeverage} was exceeded when taking into account fees." msgstr "수수료를 고려했을 때 최대 레버리지 {formattedMaxLeverage}가 초과되어 해당 포지션이 청산되었습니다." -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Swap {0}" -msgstr "{0} 스왑" - #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx @@ -4999,10 +5054,6 @@ msgstr "wstETH APR" msgid "Telegram Group" msgstr "텔레그램 그룹" -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Long TP/SL: {0} a long position when the trigger price is reached." -msgstr "{typeString} Long TP/SL: 트리거 가격에 도달할 때 {0} 롱 포지션." - #: src/components/OrderItem/TwapOrdersList/TwapOrdersList.tsx msgid "<0>{fromTokenText} {fromTokenIcon}<1> to {toTokenIcon}" msgstr "<0>{fromTokenText} {fromTokenIcon}<1>에서 {toTokenIcon}" @@ -5031,7 +5082,7 @@ msgstr "" #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/GmxAssetCard.tsx #: src/components/MarketStats/MarketGraphs.tsx #: src/components/SwapCard/SwapCard.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TradeboxMarginFields/PriceField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TVChart/Chart.tsx #: src/pages/Dashboard/GmxCard.tsx @@ -5194,6 +5245,10 @@ msgstr "브리지 TX 해시" msgid "Start unrealized pnl" msgstr "시작 미실현 PnL" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of Position)" +msgstr "" + #: src/domain/tokens/approveTokens.tsx msgid "Approval submitted! <0>View status." msgstr "승인 제출 완료! <0>상태 보기." @@ -5215,7 +5270,7 @@ msgid "Stake GMX to earn GMX rewards" msgstr "GMX를 스테이킹하여 GMX 보상을 받으세요" #: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionList/PositionList.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "TP/SL" msgstr "TP/SL" @@ -5443,6 +5498,7 @@ msgstr "거버넌스" #: src/components/OrderItem/OrderItem.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Cancel" msgstr "취소" @@ -5509,6 +5565,11 @@ msgstr "90일" msgid "Liquidation" msgstr "청산" +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Add TP/SL" +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Risk Monitoring" msgstr "GMX 위험 모니터링" @@ -5553,6 +5614,7 @@ msgid "Longs Net Rate / 1h" msgstr "롱 순 수수료율 / 1시간" #: src/components/OrderItem/OrderItem.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx msgid "Edit" msgstr "수정" @@ -5565,10 +5627,6 @@ msgstr "사용 가능한 레버리지 없음" msgid "You have a <0>pending transfer to {pendingReceiver}." msgstr "{pendingReceiver}에게 <0>대기 중인 전송이 있습니다." -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Limit size is required" -msgstr "지정가 크기가 필요합니다" - #: src/components/GmxAccountModal/DepositView.tsx msgid "From Network" msgstr "보내는 네트워크" @@ -5585,6 +5643,10 @@ msgstr "GMX 계정에서 출금" msgid "Collateral at Liquidation" msgstr "청산 시 담보" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Keep leverage at {currentLeverage}" +msgstr "" + #: src/lib/dates.ts #: src/pages/AccountDashboard/GeneralPerformanceDetails.tsx msgid "Today" @@ -5969,10 +6031,6 @@ msgstr "부정적 펀딩 수수료" msgid "{fromText} to {toExecutionText}" msgstr "{fromText}에서 {toExecutionText}로" -#: src/components/PositionSeller/PositionSeller.tsx -msgid "Create {0} Order" -msgstr "{0} 주문 생성" - #: src/context/SubaccountContext/SubaccountContextProvider.tsx msgid "Settings updated." msgstr "설정이 업데이트되었습니다." @@ -6050,11 +6108,6 @@ msgstr "{fromAmount}을 {toAmount}으로 스왑" msgid "Amount" msgstr "수량" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create {0} order" -msgstr "{0} 주문 생성" - #: src/components/PositionEditor/PositionEditor.tsx msgid "Edit {0} {1}{2}" msgstr "{0} {1}{2} 수정" @@ -6136,6 +6189,7 @@ msgstr "최소 수령이 충족될 때 주문을 실행할 유동성이 부족 #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -6242,6 +6296,7 @@ msgstr "판매 수수료" #: src/components/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Execution Details" msgstr "실행 세부 정보" @@ -6266,7 +6321,7 @@ msgstr "이익 실현 업데이트" msgid "Only addresses with over {0} in capital used are ranked.<0/><1/>The capital used is calculated as the highest value of [<2>sum of collateral of open positions - realized PnL + period start pending PnL]." msgstr "{0} 이상의 사용 자본을 가진 주소만 순위에 포함됩니다.<0/><1/>사용 자본은 [<2>오픈 포지션의 담보 합계 - 실현 PnL + 기간 시작 미결 PnL]의 최고 값으로 계산됩니다." -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Active Orders" msgstr "활성 주문" @@ -6317,6 +6372,10 @@ msgstr "프로토콜 분석" msgid "More active management required compared to GLV" msgstr "GLV에 비해 더 많은 적극적 관리가 필요합니다" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Take Profit Price" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed amount" msgstr "수수료가 금액 초과" @@ -6419,6 +6478,10 @@ msgstr "Kudai AI 에이전트" msgid "Current Price" msgstr "현재 가격" +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +msgid "Enter TP/SL price" +msgstr "" + #: src/pages/NftWallet/NftWallet.jsx msgid "Enter NFT ID" msgstr "NFT ID 입력" @@ -6465,10 +6528,6 @@ msgstr "예치" msgid "365d Est. Fees" msgstr "365일 예상 수수료" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below highest limit price" -msgstr "트리거 가격이 최고 지정가 미만" - #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/ClaimModal.tsx msgid "Select rewards to claim" msgstr "청구할 보상 선택" @@ -6536,7 +6595,9 @@ msgstr "<0>{0} 수령" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx @@ -6581,7 +6642,9 @@ msgid "We Value Your Feedback" msgstr "귀하의 피드백을 소중히 여깁니다" #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Stop Loss" msgstr "손절" @@ -6634,6 +6697,10 @@ msgstr "주문 제출 오류" msgid "Duration" msgstr "기간" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "TP Price" +msgstr "" + #: src/components/Seo/SEO.tsx msgid "Trade spot or perpetual BTC, ETH, AVAX and other top cryptocurrencies with up to 100x leverage directly from your wallet on Arbitrum and Avalanche." msgstr "Arbitrum 및 Avalanche에서 지갑에서 직접 최대 100x 레버리지로 BTC, ETH, AVAX 및 기타 상위 암호화폐를 스팟 또는 퍼페추얼 거래하세요." @@ -6790,7 +6857,8 @@ msgstr "GMX 보상:" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderItem/OrderItem.tsx #: src/components/OrderList/OrderList.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx msgid "Trigger Price" @@ -7025,6 +7093,10 @@ msgstr "시프트" msgid "Why is the claim in GLV tokens?" msgstr "청구가 GLV 토큰으로 되는 이유는 무엇인가요?" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PNL" +msgstr "" + #: src/components/SettingsModal/TradingSettings.tsx msgid "Express + One-Click" msgstr "익스프레스 + 원클릭" @@ -7091,6 +7163,8 @@ msgstr "시프트 오류." #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -7109,7 +7183,7 @@ msgstr "롱" #: src/components/PoolSelector2/PoolSelector2.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx -#: src/components/TradeBox/MarketPoolSelectorRow.tsx +#: src/components/TradeBox/MarketPoolSelectorField.tsx msgid "Pool" msgstr "풀" @@ -7202,6 +7276,8 @@ msgstr "스냅샷" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Liquidation Price" msgstr "청산 가격" @@ -7259,6 +7335,7 @@ msgstr "확인 중..." msgid "Deposited:" msgstr "입금됨:" +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Increase" @@ -7384,6 +7461,10 @@ msgstr "완료됨" msgid "Referral Terms" msgstr "추천 약관" +#: src/components/PositionDropdown/PositionDropdown.tsx +msgid "Increase Size (TWAP)" +msgstr "" + #: src/components/Earn/Portfolio/RecommendedAssets/RecommendedAssets.tsx #: src/components/GmList/FeeApyLabel.tsx msgid "Fee APY" @@ -7394,7 +7475,6 @@ msgid "Can I build on top of GMX or integrate it into my DeFi app?" msgstr "GMX 위에 빌드하거나 DeFi 앱에 통합할 수 있나요?" #: src/components/GmSwap/GmSwapBox/GmSwapWarningsRow.tsx -#: src/components/TradeBox/TradeBox.tsx msgid "Support for GMX accounts on Avalanche will be discontinued soon. Opening new positions from and depositing additional funds to Avalanche GMX accounts is no longer available. We recommend switching to Arbitrum as a settlement network." msgstr "Avalanche의 GMX 계정 지원이 곧 중단됩니다. Avalanche GMX 계정에서 새 포지션을 열거나 추가 자금을 입금하는 것은 더 이상 사용할 수 없습니다. 정산 네트워크로 Arbitrum으로 전환하는 것을 권장합니다." @@ -7437,6 +7517,10 @@ msgstr "문자, 숫자 및 언더스코어만 허용됩니다." msgid "High TWAP network fee" msgstr "높은 TWAP 네트워크 수수료" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the TP/SL button to set TP/SL orders." +msgstr "" + #: src/components/Earn/Discovery/EarnProductCard.tsx msgid "What is GM?" msgstr "GM이란 무엇인가요?" @@ -7562,6 +7646,7 @@ msgstr "<0>매 {minutes} 분{0}" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/EntryPriceRow.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Entry Price" @@ -7636,10 +7721,6 @@ msgstr "esGMX 토큰을 GMX 토큰으로 변환하세요. 볼트를 사용하기 msgid "You have an existing position in the {0} market pool.<0>Switch to {1} market pool" msgstr "{0} 시장 풀에 기존 포지션이 있습니다.<0>{1} 시장 풀로 전환" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Stop Loss PnL" -msgstr "스탑 로스 PnL" - #: src/components/TVChartContainer/constants.ts msgid "Market - Long Dec." msgstr "시장 - 롱 감소" @@ -7787,6 +7868,7 @@ msgstr "자금 청구" msgid "30d Fee APY" msgstr "30일 수수료 APY" +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "Net Price Impact / Fees" msgstr "순 가격 영향 / 수수료" @@ -7858,6 +7940,7 @@ msgstr "추천 코드 생성" #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/SwapCard/SwapCard.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "Swap" msgstr "스왑" @@ -7896,10 +7979,6 @@ msgstr "언어" msgid "Complete Transfer" msgstr "전송 완료" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below lowest limit price" -msgstr "트리거 가격이 최저 지정가 가격보다 낮음" - #: src/components/Referrals/TradersStats.tsx msgid "GMX V2 discounts are automatically applied on each trade and are not displayed on this table." msgstr "GMX V2 할인은 각 거래에서 자동 적용되며 이 테이블에 표시되지 않습니다." @@ -7976,10 +8055,6 @@ msgstr "최근 30일" msgid "Dashboards" msgstr "대시보드" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create TWAP {0} order" -msgstr "TWAP {0} 주문 생성" - #: src/components/AddressDropdown/AddressDropdownWithoutMultichain.tsx msgid "Address copied to your clipboard." msgstr "주소가 클립보드에 복사되었습니다." @@ -8006,6 +8081,10 @@ msgstr "앱 열기" msgid "7d" msgstr "7일" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "TP/SL fees" +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "Rage Trade" msgstr "레이지 트레이드" @@ -8032,6 +8111,7 @@ msgstr "전송 실패." #: src/components/OrderList/OrderList.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Mark Price" @@ -8153,7 +8233,7 @@ msgstr "매우 가능" msgid "Withdrawing funds from GMX Account..." msgstr "GMX 계정에서 자금을 출금하는 중…" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing limit order with {symbol} as collateral. <0>Switch to {symbol} collateral" msgstr "{symbol}을 담보로 한 기존 한도 주문이 있습니다. <0>{symbol} 담보로 전환" @@ -8272,7 +8352,6 @@ msgstr "로딩 중" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/TradeBox/tradeboxConstants.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx #: src/components/TradeHistory/keys.ts #: src/domain/synthetics/positions/utils.ts msgid "Limit" @@ -8324,10 +8403,9 @@ msgid "multiple asset classes" msgstr "다중 자산 클래스" #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/multichain/progress/MultichainTransferProgressView.tsx diff --git a/src/locales/pseudo/messages.po b/src/locales/pseudo/messages.po index 2e7b5787d3..7f0b8c021c 100644 --- a/src/locales/pseudo/messages.po +++ b/src/locales/pseudo/messages.po @@ -73,6 +73,11 @@ msgstr "" msgid "receive" msgstr "" +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx +msgid "Create basic TP/SL orders that fully close your position. For advanced TP/SL setup, use the positions list after opening a position." +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Referrals Dashboard" msgstr "" @@ -133,7 +138,7 @@ msgstr "" msgid "Arbitrum" msgstr "" -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Orders ({0})" msgstr "" @@ -148,6 +153,8 @@ msgstr "" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/Referrals/AffiliatesStats.tsx #: src/components/Referrals/TradersStats.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx msgid "Type" @@ -264,6 +271,10 @@ msgstr "" msgid "Discover" msgstr "" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Full Position Close" +msgstr "" + #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "There is no price impact for increase orders, orders are filled at the mark price. <0>Read more." msgstr "" @@ -428,7 +439,6 @@ msgstr "" #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price below mark price" @@ -641,6 +651,10 @@ msgstr "" msgid "Market order will be cancellable in {minutesText}{seconds}s." msgstr "" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Edit TP/SL Size" +msgstr "" + #: src/components/UserIncentiveDistribution/AboutGlpIncident.tsx msgid "About GLP Incident" msgstr "" @@ -675,17 +689,19 @@ msgid "Settling" msgstr "" #: src/components/PositionDropdown/PositionDropdown.tsx -#: src/components/PositionItem/PositionItem.tsx msgid "Edit Collateral" msgstr "" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "SL Price" +msgstr "" + #: src/components/Earn/Discovery/EarnFaq.tsx msgid "GLV and GM both enable liquidity provision on GMX, allowing users to earn fees from three revenue sources that auto-compound into the token's price. GM tokens represent liquidity in a single pool that supports one specific market on GMX. GLV vaults contain multiple GM tokens in an auto-rebalancing composition, thereby aggregating liquidity across various markets that have the same backing tokens (ex: ETH-USDC, or BTC-USDC). GM enables users to provide isolated liquidity with exposure to a specific market, while GLV allows users to passively earn yield from a diverse range of highly utilized pools." msgstr "" #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price above mark price" @@ -862,6 +878,10 @@ msgstr "" msgid "MAX" msgstr "" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Stop Loss Price" +msgstr "" + #: src/pages/BuyGMX/BuyGMX.tsx msgid "No centralized exchanges available for this network." msgstr "" @@ -929,6 +949,8 @@ msgstr "" #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx msgid "Pay" msgstr "" @@ -1111,6 +1133,7 @@ msgstr "" #: src/components/OrderItem/OrderItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Collateral" msgstr "" @@ -1316,6 +1339,10 @@ msgstr "" msgid "Wallet is not connected" msgstr "" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of position)" +msgstr "" + #: landing/src/pages/Home/HeroSection/AnimatedTitle.tsx msgid "100+ crypto tokens" msgstr "" @@ -1338,6 +1365,10 @@ msgstr "" msgid "The current price impact is {0}. Consider adding a buffer of 0.30% to it so the order is more likely to be processed." msgstr "" +#: src/components/TPSLModal/TPSLModal.tsx +msgid "TP/SL: {positionTitle}" +msgstr "" + #: src/components/SettingsModal/RpcDebugSettings.tsx msgid "Public" msgstr "" @@ -1444,6 +1475,10 @@ msgstr "" msgid "The amount you are trying to withdraw exceeds the limit. Please try an amount smaller than <0>{upperLimitFormatted}." msgstr "" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Gain" +msgstr "" + #: src/components/NotifyModal/NotifyModal.tsx msgid "Discover GMX Alerts" msgstr "" @@ -1668,10 +1703,6 @@ msgstr "" msgid "RPnL" msgstr "" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined stop losses are at maximum (100%). Decrease existing values to add more orders." -msgstr "" - #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx msgid "4-18 Places" @@ -1768,7 +1799,6 @@ msgid "Min size per part: {0}" msgstr "" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price above liq. price" msgstr "" @@ -1800,7 +1830,6 @@ msgstr "" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionSeller/PositionSeller.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Mark" @@ -1942,6 +1971,7 @@ msgstr "" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeboxMarginFields/MarginPercentageSlider.tsx msgid "Max" msgstr "" @@ -2020,9 +2050,9 @@ msgstr "" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx +#: src/components/TradeboxMarginFields/SizeField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -2174,6 +2204,7 @@ msgstr "" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Initial collateral (collateral excluding borrow and funding fee)." msgstr "" @@ -2435,10 +2466,6 @@ msgstr "" msgid "The execution price may vary from your set limit price due to fees and price impact, ensuring you receive the displayed minimum receive amount. <0>Read more." msgstr "" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above lowest limit price" -msgstr "" - #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2528,6 +2555,10 @@ msgstr "" msgid "Fees (Incl. Swap)" msgstr "" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "Order update fee" +msgstr "" + #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts msgid "{fromText} to {toMinText}" @@ -2614,6 +2645,7 @@ msgid "Exposure to Backing Tokens" msgstr "" #: src/components/Referrals/AddAffiliateCode.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Creating..." msgstr "" @@ -2669,7 +2701,9 @@ msgstr "" msgid "Returned Collateral" msgstr "" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "There are issues in the TP/SL orders." msgstr "" @@ -2762,10 +2796,6 @@ msgstr "" msgid "GMX bonds can be bought on Bond Protocol with a discount and a small vesting period:" msgstr "" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above highest limit price" -msgstr "" - #: src/components/Earn/Discovery/EarnFaq.tsx msgid "Yes, staking and providing liquidity are fully permissionless. The GMX, GLV, and GM tokens are liquid and can be unstaked and withdrawn at any time. For LP tokens, there could be some rare edge cases where your liquidity is being used for active trades. In that case, you can withdraw as soon as positions close." msgstr "" @@ -2895,12 +2925,16 @@ msgstr "" msgid "No eligible tokens available on {0} for deposit" msgstr "" +#: src/components/TPSLModal/TPSLInputRow.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PnL" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Max close amount exceeded" msgstr "" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2986,6 +3020,7 @@ msgstr "" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx @@ -3025,7 +3060,8 @@ msgstr "" #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Claims/ClaimsHistory.tsx #: src/components/OrderItem/OrderItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/Referrals/ClaimAffiliatesModal/ClaimAffiliatesModal.tsx #: src/components/TableMarketFilter/MarketFilterBase.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx @@ -3114,7 +3150,6 @@ msgid "Debug values are not available" msgstr "" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price below liq. price" msgstr "" @@ -3247,10 +3282,6 @@ msgstr "" msgid "V1 esGMX" msgstr "" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Take Profit PnL" -msgstr "" - #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your GLV or GM tokens, and borrow against them, or put your LP tokens into strategies to maximize your yield." msgstr "" @@ -3295,6 +3326,7 @@ msgstr "" #: src/components/Earn/AdditionalOpportunities/OpportunityFilters.tsx #: src/components/GmxAccountModal/AvailableToTradeAssetsView.tsx #: src/components/TokenSelector/MultichainMarketTokenSelector.tsx +#: src/components/TPSLModal/TPSLModal.tsx msgid "All" msgstr "" @@ -3321,7 +3353,8 @@ msgstr "" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/domain/synthetics/positions/utils.ts msgid "TWAP" @@ -3401,10 +3434,6 @@ msgstr "" msgid "{longOrShort} positions {fundingAction} a funding fee of {fundingRate} per hour and do not pay a borrow fee." msgstr "" -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Short TP/SL: {0} a short position when the trigger price is reached." -msgstr "" - #: src/pages/Ecosystem/Ecosystem.tsx msgid "Community-led Telegram groups." msgstr "" @@ -3438,6 +3467,10 @@ msgstr "" msgid "Insufficient liquidity in the {0} market pool. Select a different pool for this market.{1}" msgstr "" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Loss" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "App disabled, pending {0} upgrade" msgstr "" @@ -3472,6 +3505,10 @@ msgstr "" msgid "<0>every {hours} hours{0}" msgstr "" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "{secondLabel}" +msgstr "" + #: src/components/DebugExpressSettings/DebugSwapsSettings.tsx #: src/components/DebugSwapsSettings/DebugSwapsSettings.tsx msgid "Fail External Swaps" @@ -3501,13 +3538,17 @@ msgstr "" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/pages/AccountDashboard/DailyAndCumulativePnL.tsx msgid "PnL" msgstr "" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "No TP/SL orders" +msgstr "" + #: src/components/TradeBox/ExpressTradingWarningCard.tsx msgid "Express Trading is not available using network's native token {0}. Consider using {1} instead." msgstr "" @@ -3773,7 +3814,6 @@ msgstr "" #: src/components/CollateralSelector/CollateralSelector.tsx #: src/components/CollateralSelector/PositionEditorCollateralSelector.tsx -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx msgid "Collateral In" msgstr "" @@ -3785,6 +3825,10 @@ msgstr "" msgid "Your opinions and experiences matter to us. Your feedback helps us understand what we are doing well and where we can make enhancements." msgstr "" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Create TP/SL" +msgstr "" + #: src/components/GmxAccountModal/DepositView.tsx #: src/components/GmxAccountModal/WithdrawalView.tsx msgid "Estimated Time" @@ -3823,6 +3867,10 @@ msgstr "" msgid "Transfer account" msgstr "" +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Cancel all" +msgstr "" + #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your tokens, or borrow against it." @@ -3845,8 +3893,6 @@ msgstr "" #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx msgid "Read more" msgstr "" @@ -3915,6 +3961,7 @@ msgstr "" #: src/components/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/AllowedSwapSlippageInputRowImpl/AllowedSwapSlippageInputRowImpl.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/pages/LeaderboardPage/components/LeaderboardAccountsTable.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -3941,6 +3988,8 @@ msgstr "" #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -4009,10 +4058,6 @@ msgstr "" msgid "Links" msgstr "" -#: src/components/PositionItem/PositionItem.tsx -msgid "Use the \"Close\" button to reduce your position via market, TP/SL, or TWAP orders." -msgstr "" - #: src/components/Seo/SEO.tsx msgid "GMX | Decentralized Perpetual Exchange" msgstr "" @@ -4227,7 +4272,7 @@ msgstr "" msgid "Decentralized Options Strategies" msgstr "" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This action will not apply for that position. <0>Switch to {1} collateral" msgstr "" @@ -4267,7 +4312,9 @@ msgid "Amount of traders you referred." msgstr "" #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Take Profit" msgstr "" @@ -4285,6 +4332,14 @@ msgstr "" msgid "Insufficient {0} liquidity" msgstr "" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Unable to calculate order" +msgstr "" + +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "TP/SL: {positionTitle} Decrease" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.tsx #: src/pages/Ecosystem/Ecosystem.tsx msgid "Creator" @@ -4310,6 +4365,10 @@ msgstr "" msgid "Transfer already initiated" msgstr "" +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +msgid "Margin to Pay" +msgstr "" + #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx msgid "Withdraw submitted." msgstr "" @@ -4387,7 +4446,8 @@ msgstr "" msgid "Accrued price impact rebates. They will become claimable after 5 days.<0/><1/><2>Read more." msgstr "" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "Take Profit / Stop Loss" msgstr "" @@ -4483,6 +4543,10 @@ msgstr "" msgid "RPC Monitoring" msgstr "" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the \"Close\" button to reduce your position via market or TWAP orders." +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "ODOS" msgstr "" @@ -4544,10 +4608,6 @@ msgstr "" msgid "<0>Solana Support<1>Botanix Support<2>GMX Express" msgstr "" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined take profits are at maximum (100%). Decrease existing values to add more orders." -msgstr "" - #: src/components/GmList/GmList.tsx msgid "POOL" msgstr "" @@ -4580,6 +4640,8 @@ msgstr "" msgid "Recommended" msgstr "" +#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Decrease" @@ -4783,11 +4845,12 @@ msgstr "" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Collateral ({0})" msgstr "" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This order will not be valid for that position. <0>Switch to {1} collateral" msgstr "" @@ -4890,10 +4953,6 @@ msgstr "" msgid "Swap Price Impact for External Swap Threshold" msgstr "" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Limit / Take Profit / Stop Loss" -msgstr "" - #: landing/src/pages/Home/LiqiuditySection/PoolCards.tsx msgid "Steady returns without management" msgstr "" @@ -4906,10 +4965,6 @@ msgstr "" msgid "This position was liquidated as the max. leverage of {formattedMaxLeverage} was exceeded when taking into account fees." msgstr "" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Swap {0}" -msgstr "" - #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx @@ -4999,10 +5054,6 @@ msgstr "" msgid "Telegram Group" msgstr "" -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Long TP/SL: {0} a long position when the trigger price is reached." -msgstr "" - #: src/components/OrderItem/TwapOrdersList/TwapOrdersList.tsx msgid "<0>{fromTokenText} {fromTokenIcon}<1> to {toTokenIcon}" msgstr "" @@ -5031,7 +5082,7 @@ msgstr "" #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/GmxAssetCard.tsx #: src/components/MarketStats/MarketGraphs.tsx #: src/components/SwapCard/SwapCard.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TradeboxMarginFields/PriceField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TVChart/Chart.tsx #: src/pages/Dashboard/GmxCard.tsx @@ -5194,6 +5245,10 @@ msgstr "" msgid "Start unrealized pnl" msgstr "" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of Position)" +msgstr "" + #: src/domain/tokens/approveTokens.tsx msgid "Approval submitted! <0>View status." msgstr "" @@ -5215,7 +5270,7 @@ msgid "Stake GMX to earn GMX rewards" msgstr "" #: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionList/PositionList.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "TP/SL" msgstr "" @@ -5443,6 +5498,7 @@ msgstr "" #: src/components/OrderItem/OrderItem.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Cancel" msgstr "" @@ -5509,6 +5565,11 @@ msgstr "" msgid "Liquidation" msgstr "" +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Add TP/SL" +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Risk Monitoring" msgstr "" @@ -5553,6 +5614,7 @@ msgid "Longs Net Rate / 1h" msgstr "" #: src/components/OrderItem/OrderItem.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx msgid "Edit" msgstr "" @@ -5565,10 +5627,6 @@ msgstr "" msgid "You have a <0>pending transfer to {pendingReceiver}." msgstr "" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Limit size is required" -msgstr "" - #: src/components/GmxAccountModal/DepositView.tsx msgid "From Network" msgstr "" @@ -5585,6 +5643,10 @@ msgstr "" msgid "Collateral at Liquidation" msgstr "" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Keep leverage at {currentLeverage}" +msgstr "" + #: src/lib/dates.ts #: src/pages/AccountDashboard/GeneralPerformanceDetails.tsx msgid "Today" @@ -5969,10 +6031,6 @@ msgstr "" msgid "{fromText} to {toExecutionText}" msgstr "" -#: src/components/PositionSeller/PositionSeller.tsx -msgid "Create {0} Order" -msgstr "" - #: src/context/SubaccountContext/SubaccountContextProvider.tsx msgid "Settings updated." msgstr "" @@ -6050,11 +6108,6 @@ msgstr "" msgid "Amount" msgstr "" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create {0} order" -msgstr "" - #: src/components/PositionEditor/PositionEditor.tsx msgid "Edit {0} {1}{2}" msgstr "" @@ -6136,6 +6189,7 @@ msgstr "" #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -6242,6 +6296,7 @@ msgstr "" #: src/components/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Execution Details" msgstr "" @@ -6266,7 +6321,7 @@ msgstr "" msgid "Only addresses with over {0} in capital used are ranked.<0/><1/>The capital used is calculated as the highest value of [<2>sum of collateral of open positions - realized PnL + period start pending PnL]." msgstr "" -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Active Orders" msgstr "" @@ -6317,6 +6372,10 @@ msgstr "" msgid "More active management required compared to GLV" msgstr "" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Take Profit Price" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed amount" msgstr "" @@ -6419,6 +6478,10 @@ msgstr "" msgid "Current Price" msgstr "" +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +msgid "Enter TP/SL price" +msgstr "" + #: src/pages/NftWallet/NftWallet.jsx msgid "Enter NFT ID" msgstr "" @@ -6465,10 +6528,6 @@ msgstr "" msgid "365d Est. Fees" msgstr "" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below highest limit price" -msgstr "" - #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/ClaimModal.tsx msgid "Select rewards to claim" msgstr "" @@ -6536,7 +6595,9 @@ msgstr "" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx @@ -6581,7 +6642,9 @@ msgid "We Value Your Feedback" msgstr "" #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Stop Loss" msgstr "" @@ -6634,6 +6697,10 @@ msgstr "" msgid "Duration" msgstr "" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "TP Price" +msgstr "" + #: src/components/Seo/SEO.tsx msgid "Trade spot or perpetual BTC, ETH, AVAX and other top cryptocurrencies with up to 100x leverage directly from your wallet on Arbitrum and Avalanche." msgstr "" @@ -6790,7 +6857,8 @@ msgstr "" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderItem/OrderItem.tsx #: src/components/OrderList/OrderList.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx msgid "Trigger Price" @@ -7025,6 +7093,10 @@ msgstr "" msgid "Why is the claim in GLV tokens?" msgstr "" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PNL" +msgstr "" + #: src/components/SettingsModal/TradingSettings.tsx msgid "Express + One-Click" msgstr "" @@ -7091,6 +7163,8 @@ msgstr "" #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -7109,7 +7183,7 @@ msgstr "" #: src/components/PoolSelector2/PoolSelector2.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx -#: src/components/TradeBox/MarketPoolSelectorRow.tsx +#: src/components/TradeBox/MarketPoolSelectorField.tsx msgid "Pool" msgstr "" @@ -7202,6 +7276,8 @@ msgstr "" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Liquidation Price" msgstr "" @@ -7259,6 +7335,7 @@ msgstr "" msgid "Deposited:" msgstr "" +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Increase" @@ -7384,6 +7461,10 @@ msgstr "" msgid "Referral Terms" msgstr "" +#: src/components/PositionDropdown/PositionDropdown.tsx +msgid "Increase Size (TWAP)" +msgstr "" + #: src/components/Earn/Portfolio/RecommendedAssets/RecommendedAssets.tsx #: src/components/GmList/FeeApyLabel.tsx msgid "Fee APY" @@ -7394,7 +7475,6 @@ msgid "Can I build on top of GMX or integrate it into my DeFi app?" msgstr "" #: src/components/GmSwap/GmSwapBox/GmSwapWarningsRow.tsx -#: src/components/TradeBox/TradeBox.tsx msgid "Support for GMX accounts on Avalanche will be discontinued soon. Opening new positions from and depositing additional funds to Avalanche GMX accounts is no longer available. We recommend switching to Arbitrum as a settlement network." msgstr "" @@ -7437,6 +7517,10 @@ msgstr "" msgid "High TWAP network fee" msgstr "" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the TP/SL button to set TP/SL orders." +msgstr "" + #: src/components/Earn/Discovery/EarnProductCard.tsx msgid "What is GM?" msgstr "" @@ -7562,6 +7646,7 @@ msgstr "" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/EntryPriceRow.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Entry Price" @@ -7636,10 +7721,6 @@ msgstr "" msgid "You have an existing position in the {0} market pool.<0>Switch to {1} market pool" msgstr "" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Stop Loss PnL" -msgstr "" - #: src/components/TVChartContainer/constants.ts msgid "Market - Long Dec." msgstr "" @@ -7787,6 +7868,7 @@ msgstr "" msgid "30d Fee APY" msgstr "" +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "Net Price Impact / Fees" msgstr "" @@ -7858,6 +7940,7 @@ msgstr "" #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/SwapCard/SwapCard.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "Swap" msgstr "" @@ -7896,10 +7979,6 @@ msgstr "" msgid "Complete Transfer" msgstr "" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below lowest limit price" -msgstr "" - #: src/components/Referrals/TradersStats.tsx msgid "GMX V2 discounts are automatically applied on each trade and are not displayed on this table." msgstr "" @@ -7976,10 +8055,6 @@ msgstr "" msgid "Dashboards" msgstr "" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create TWAP {0} order" -msgstr "" - #: src/components/AddressDropdown/AddressDropdownWithoutMultichain.tsx msgid "Address copied to your clipboard." msgstr "" @@ -8006,6 +8081,10 @@ msgstr "" msgid "7d" msgstr "" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "TP/SL fees" +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "Rage Trade" msgstr "" @@ -8032,6 +8111,7 @@ msgstr "" #: src/components/OrderList/OrderList.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Mark Price" @@ -8153,7 +8233,7 @@ msgstr "" msgid "Withdrawing funds from GMX Account..." msgstr "" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing limit order with {symbol} as collateral. <0>Switch to {symbol} collateral" msgstr "" @@ -8272,7 +8352,6 @@ msgstr "" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/TradeBox/tradeboxConstants.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx #: src/components/TradeHistory/keys.ts #: src/domain/synthetics/positions/utils.ts msgid "Limit" @@ -8324,10 +8403,9 @@ msgid "multiple asset classes" msgstr "" #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/multichain/progress/MultichainTransferProgressView.tsx diff --git a/src/locales/ru/messages.po b/src/locales/ru/messages.po index eeeaf49c82..673d46ed70 100644 --- a/src/locales/ru/messages.po +++ b/src/locales/ru/messages.po @@ -73,6 +73,11 @@ msgstr "ЦЕНА" msgid "receive" msgstr "получить" +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx +msgid "Create basic TP/SL orders that fully close your position. For advanced TP/SL setup, use the positions list after opening a position." +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Referrals Dashboard" msgstr "Панель рефералов GMX" @@ -133,7 +138,7 @@ msgstr "Оптимизатор доходности на Avalanche" msgid "Arbitrum" msgstr "Arbitrum" -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Orders ({0})" msgstr "Ордера ({0})" @@ -148,6 +153,8 @@ msgstr "стоп-лосс" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/Referrals/AffiliatesStats.tsx #: src/components/Referrals/TradersStats.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx msgid "Type" @@ -264,6 +271,10 @@ msgstr "Исполнение рыночного обмена" msgid "Discover" msgstr "Обзор" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Full Position Close" +msgstr "" + #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "There is no price impact for increase orders, orders are filled at the mark price. <0>Read more." msgstr "Для ордеров на увеличение нет влияния на цену. Ордера исполняются по маркетной цене. <0>Подробнее." @@ -428,7 +439,6 @@ msgstr "Максимальный USD пула превышен" #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price below mark price" @@ -641,6 +651,10 @@ msgstr "Контакт в Telegram (опционально)" msgid "Market order will be cancellable in {minutesText}{seconds}s." msgstr "Рыночный ордер можно отменить через {minutesText}{seconds}с." +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Edit TP/SL Size" +msgstr "" + #: src/components/UserIncentiveDistribution/AboutGlpIncident.tsx msgid "About GLP Incident" msgstr "О инциденте GLP" @@ -675,17 +689,19 @@ msgid "Settling" msgstr "Расчет" #: src/components/PositionDropdown/PositionDropdown.tsx -#: src/components/PositionItem/PositionItem.tsx msgid "Edit Collateral" msgstr "Редактирование залога" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "SL Price" +msgstr "" + #: src/components/Earn/Discovery/EarnFaq.tsx msgid "GLV and GM both enable liquidity provision on GMX, allowing users to earn fees from three revenue sources that auto-compound into the token's price. GM tokens represent liquidity in a single pool that supports one specific market on GMX. GLV vaults contain multiple GM tokens in an auto-rebalancing composition, thereby aggregating liquidity across various markets that have the same backing tokens (ex: ETH-USDC, or BTC-USDC). GM enables users to provide isolated liquidity with exposure to a specific market, while GLV allows users to passively earn yield from a diverse range of highly utilized pools." msgstr "GLV и GM позволяют предоставлять ликвидность на GMX и зарабатывать комиссии из трёх источников дохода, которые автоматически реинвестируются в цену токена. Токены GM представляют ликвидность в одном пуле, обслуживающем конкретный рынок GMX. Хранилища GLV содержат несколько токенов GM в автоматически ребалансируемой композиции, тем самым агрегируя ликвидность по разным рынкам с одинаковыми базовыми токенами (например: ETH-USDC или BTC-USDC). GM даёт возможность предоставлять изолированную ликвидность с экспозицией к конкретному рынку, тогда как GLV позволяет пассивно получать доход от широкого спектра наиболее используемых пулов." #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price above mark price" @@ -862,6 +878,10 @@ msgstr "Вывести из стейкинга {tokenSymbol}" msgid "MAX" msgstr "МАКС" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Stop Loss Price" +msgstr "" + #: src/pages/BuyGMX/BuyGMX.tsx msgid "No centralized exchanges available for this network." msgstr "Нет централизованных бирж для этой сети." @@ -929,6 +949,8 @@ msgstr "Вы собираетесь перейти на GMX Solana, которы #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx msgid "Pay" msgstr "Оплатить" @@ -1111,6 +1133,7 @@ msgstr "Разрешить перевод всех моих токенов на #: src/components/OrderItem/OrderItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Collateral" msgstr "Залог" @@ -1316,6 +1339,10 @@ msgstr "Мин. ордер: {0}" msgid "Wallet is not connected" msgstr "Кошелёк не подключён" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of position)" +msgstr "" + #: landing/src/pages/Home/HeroSection/AnimatedTitle.tsx msgid "100+ crypto tokens" msgstr "Более 100 криптотокенов" @@ -1338,6 +1365,10 @@ msgstr "GM - токен провайдера ликвидности для ры msgid "The current price impact is {0}. Consider adding a buffer of 0.30% to it so the order is more likely to be processed." msgstr "Текущее влияние на цену {0}. Рассмотрите добавление буфера 0.30%, чтобы ордер был более вероятно обработан." +#: src/components/TPSLModal/TPSLModal.tsx +msgid "TP/SL: {positionTitle}" +msgstr "" + #: src/components/SettingsModal/RpcDebugSettings.tsx msgid "Public" msgstr "Публичный" @@ -1444,6 +1475,10 @@ msgstr "Время" msgid "The amount you are trying to withdraw exceeds the limit. Please try an amount smaller than <0>{upperLimitFormatted}." msgstr "Сумма, которую вы пытаетесь вывести, превышает лимит. Пожалуйста, попробуйте сумму меньше <0>{upperLimitFormatted}." +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Gain" +msgstr "" + #: src/components/NotifyModal/NotifyModal.tsx msgid "Discover GMX Alerts" msgstr "Откройте GMX Alerts" @@ -1668,10 +1703,6 @@ msgstr "Путь обмена не найден" msgid "RPnL" msgstr "Реализованный PnL" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined stop losses are at maximum (100%). Decrease existing values to add more orders." -msgstr "Комбинированные стоп-лоссы на максимуме (100%). Уменьшите существующие значения, чтобы добавить больше ордеров." - #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx msgid "4-18 Places" @@ -1768,7 +1799,6 @@ msgid "Min size per part: {0}" msgstr "Мин. размер на часть: {0}" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price above liq. price" msgstr "Триггерная цена выше цены ликв." @@ -1800,7 +1830,6 @@ msgstr "Хранилища на основе опций" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionSeller/PositionSeller.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Mark" @@ -1942,6 +1971,7 @@ msgstr "Стоимость комиссий в USD рассчитывается #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeboxMarginFields/MarginPercentageSlider.tsx msgid "Max" msgstr "Макс" @@ -2020,9 +2050,9 @@ msgstr "Купить GMX на {chainName}" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx +#: src/components/TradeboxMarginFields/SizeField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -2174,6 +2204,7 @@ msgstr "Текущие эндпоинты ({0})" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Initial collateral (collateral excluding borrow and funding fee)." msgstr "Начальный коллатераль (коллатераль без учета заимствования и комиссии финансирования)." @@ -2435,10 +2466,6 @@ msgstr "Бридж и обмен" msgid "The execution price may vary from your set limit price due to fees and price impact, ensuring you receive the displayed minimum receive amount. <0>Read more." msgstr "Цена исполнения может отличаться от установленной лимитной цены из-за комиссий и влияния на цену, гарантируя получение отображаемой минимальной суммы. <0>Подробнее." -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above lowest limit price" -msgstr "Триггерная цена выше минимальной лимитной цены" - #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2528,6 +2555,10 @@ msgstr "Годовая Процентная Ставка обновляется msgid "Fees (Incl. Swap)" msgstr "Комиссии (Вкл. Обмен)" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "Order update fee" +msgstr "" + #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts msgid "{fromText} to {toMinText}" @@ -2614,6 +2645,7 @@ msgid "Exposure to Backing Tokens" msgstr "Экспозиция к Токенам Поддержки" #: src/components/Referrals/AddAffiliateCode.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Creating..." msgstr "Создание…" @@ -2669,7 +2701,9 @@ msgstr "В других токенах:" msgid "Returned Collateral" msgstr "Возвращённый Залог" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "There are issues in the TP/SL orders." msgstr "Есть проблемы в ордерах TP/SL." @@ -2762,10 +2796,6 @@ msgstr "Купить GMX на централизованных биржах:" msgid "GMX bonds can be bought on Bond Protocol with a discount and a small vesting period:" msgstr "Облигации GMX можно купить на Bond Protocol со скидкой и небольшим периодом вестинга:" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above highest limit price" -msgstr "Триггерная цена выше максимальной лимитной цены" - #: src/components/Earn/Discovery/EarnFaq.tsx msgid "Yes, staking and providing liquidity are fully permissionless. The GMX, GLV, and GM tokens are liquid and can be unstaked and withdrawn at any time. For LP tokens, there could be some rare edge cases where your liquidity is being used for active trades. In that case, you can withdraw as soon as positions close." msgstr "Да, стейкинг и предоставление ликвидности полностью без разрешений. Токены GMX, GLV и GM ликвидны и могут быть выведены из стейкинга в любой момент. В редких случаях, когда ваша ликвидность используется в открытых позициях, вывод станет доступен сразу после их закрытия." @@ -2895,12 +2925,16 @@ msgstr "<0>Продажа {0}{1}<1>{poolName}" msgid "No eligible tokens available on {0} for deposit" msgstr "Нет подходящих токенов на {0} для внесения" +#: src/components/TPSLModal/TPSLInputRow.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PnL" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Max close amount exceeded" msgstr "Макс. сумма закрытия превышена" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2986,6 +3020,7 @@ msgstr "20с" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx @@ -3025,7 +3060,8 @@ msgstr "Недостаточно доступной ликвидности. Ор #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Claims/ClaimsHistory.tsx #: src/components/OrderItem/OrderItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/Referrals/ClaimAffiliatesModal/ClaimAffiliatesModal.tsx #: src/components/TableMarketFilter/MarketFilterBase.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx @@ -3114,7 +3150,6 @@ msgid "Debug values are not available" msgstr "Значения отладки недоступны" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price below liq. price" msgstr "Триггерная цена ниже ликв. цены" @@ -3247,10 +3282,6 @@ msgstr "Средн. Размер" msgid "V1 esGMX" msgstr "V1 esGMX" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Take Profit PnL" -msgstr "PnL Take Profit" - #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your GLV or GM tokens, and borrow against them, or put your LP tokens into strategies to maximize your yield." msgstr "Сдавайте в долг свои токены GLV или GM, берите кредит под залог или используйте LP-токены в стратегиях для максимизации доходности." @@ -3295,6 +3326,7 @@ msgstr "Требование..." #: src/components/Earn/AdditionalOpportunities/OpportunityFilters.tsx #: src/components/GmxAccountModal/AvailableToTradeAssetsView.tsx #: src/components/TokenSelector/MultichainMarketTokenSelector.tsx +#: src/components/TPSLModal/TPSLModal.tsx msgid "All" msgstr "Все" @@ -3321,7 +3353,8 @@ msgstr "Позиции {longOrShort} {fundingAction} комиссию за фи #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/domain/synthetics/positions/utils.ts msgid "TWAP" @@ -3401,10 +3434,6 @@ msgstr "Допустимое Скольжение" msgid "{longOrShort} positions {fundingAction} a funding fee of {fundingRate} per hour and do not pay a borrow fee." msgstr "Позиции {longOrShort} {fundingAction} комиссию за финансирование {fundingRate} в час и не платят комиссию за заимствование." -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Short TP/SL: {0} a short position when the trigger price is reached." -msgstr "{typeString} Шорт TP/SL: {0} шорт позицию, когда достигнута триггерная цена." - #: src/pages/Ecosystem/Ecosystem.tsx msgid "Community-led Telegram groups." msgstr "Группы Telegram под руководством сообщества." @@ -3438,6 +3467,10 @@ msgstr "Запрос на Рыночный Обмен" msgid "Insufficient liquidity in the {0} market pool. Select a different pool for this market.{1}" msgstr "Недостаточно ликвидности в пуле рынка {0}. Выберите другой пул для этого рынка.{1}" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Loss" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "App disabled, pending {0} upgrade" msgstr "Приложение отключено, ожидается обновление {0}" @@ -3472,6 +3505,10 @@ msgstr "Успешный клейм комиссий финансировани msgid "<0>every {hours} hours{0}" msgstr "<0>каждые {hours} часов{0}" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "{secondLabel}" +msgstr "" + #: src/components/DebugExpressSettings/DebugSwapsSettings.tsx #: src/components/DebugSwapsSettings/DebugSwapsSettings.tsx msgid "Fail External Swaps" @@ -3501,13 +3538,17 @@ msgstr "Обмен отправлен." #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/pages/AccountDashboard/DailyAndCumulativePnL.tsx msgid "PnL" msgstr "PnL" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "No TP/SL orders" +msgstr "" + #: src/components/TradeBox/ExpressTradingWarningCard.tsx msgid "Express Trading is not available using network's native token {0}. Consider using {1} instead." msgstr "Экспресс-Торговля недоступна с использованием нативного токена сети {0}. Рассмотрите использование {1} вместо этого." @@ -3773,7 +3814,6 @@ msgstr "Заработок" #: src/components/CollateralSelector/CollateralSelector.tsx #: src/components/CollateralSelector/PositionEditorCollateralSelector.tsx -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx msgid "Collateral In" msgstr "Залог Внесён" @@ -3785,6 +3825,10 @@ msgstr "Рынки GM" msgid "Your opinions and experiences matter to us. Your feedback helps us understand what we are doing well and where we can make enhancements." msgstr "Ваши мнения и опыт важны для нас. Ваш отзыв помогает нам понять, что мы делаем хорошо и где мы можем улучшиться." +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Create TP/SL" +msgstr "" + #: src/components/GmxAccountModal/DepositView.tsx #: src/components/GmxAccountModal/WithdrawalView.tsx msgid "Estimated Time" @@ -3823,6 +3867,10 @@ msgstr "Рибейты, заработанные этим аккаунтом в msgid "Transfer account" msgstr "Перевести аккаунт" +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Cancel all" +msgstr "" + #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your tokens, or borrow against it." @@ -3845,8 +3893,6 @@ msgstr "<0>GMX исполняет сделки против динамическ #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx msgid "Read more" msgstr "Подробнее" @@ -3915,6 +3961,7 @@ msgstr "Ещё возможности" #: src/components/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/AllowedSwapSlippageInputRowImpl/AllowedSwapSlippageInputRowImpl.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/pages/LeaderboardPage/components/LeaderboardAccountsTable.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -3941,6 +3988,8 @@ msgstr "Триггерная цена для ордера." #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -4009,10 +4058,6 @@ msgstr "Оповещения о Ценах Торговых Пар" msgid "Links" msgstr "Ссылки" -#: src/components/PositionItem/PositionItem.tsx -msgid "Use the \"Close\" button to reduce your position via market, TP/SL, or TWAP orders." -msgstr "Используйте кнопку «Закрыть» для уменьшения позиции через рыночные, TP/SL или TWAP ордера." - #: src/components/Seo/SEO.tsx msgid "GMX | Decentralized Perpetual Exchange" msgstr "GMX | Децентрализованная Биржа Перпетуалов" @@ -4227,7 +4272,7 @@ msgstr "Вакансии" msgid "Decentralized Options Strategies" msgstr "Децентрализованные Опционные Стратегии" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This action will not apply for that position. <0>Switch to {1} collateral" msgstr "У вас есть существующая позиция с {0} как коллатералем. Это действие не применится к этой позиции. <0>Переключиться на коллатераль {1}" @@ -4267,7 +4312,9 @@ msgid "Amount of traders you referred." msgstr "Количество трейдеров, которых вы пригласили." #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Take Profit" msgstr "Take Profit" @@ -4285,6 +4332,14 @@ msgstr "{typeString} Long Stop Market: {0} длинную позицию, ког msgid "Insufficient {0} liquidity" msgstr "Недостаточно ликвидности {0}" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Unable to calculate order" +msgstr "" + +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "TP/SL: {positionTitle} Decrease" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.tsx #: src/pages/Ecosystem/Ecosystem.tsx msgid "Creator" @@ -4310,6 +4365,10 @@ msgstr "Создать Лимит" msgid "Transfer already initiated" msgstr "Перевод уже запущен" +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +msgid "Margin to Pay" +msgstr "" + #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx msgid "Withdraw submitted." msgstr "Вывод отправлен." @@ -4387,7 +4446,8 @@ msgstr "Botanix" msgid "Accrued price impact rebates. They will become claimable after 5 days.<0/><1/><2>Read more." msgstr "Накопленные возмещения влияния цены. Они станут клеймабельными через 5 дней.<0/><1/><2>Подробнее." -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "Take Profit / Stop Loss" msgstr "Take Profit / Stop Loss" @@ -4483,6 +4543,10 @@ msgstr "Рынки не найдены." msgid "RPC Monitoring" msgstr "Мониторинг RPC" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the \"Close\" button to reduce your position via market or TWAP orders." +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "ODOS" msgstr "ODOS" @@ -4544,10 +4608,6 @@ msgstr "Высокое влияние на цену" msgid "<0>Solana Support<1>Botanix Support<2>GMX Express" msgstr "<0>Поддержка Solana<1>Поддержка Botanix<2>GMX Express" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined take profits are at maximum (100%). Decrease existing values to add more orders." -msgstr "Комбинированные take profit на максимуме (100%). Уменьшите существующие значения, чтобы добавить больше ордеров." - #: src/components/GmList/GmList.tsx msgid "POOL" msgstr "ПУЛ" @@ -4580,6 +4640,8 @@ msgstr "Продажа" msgid "Recommended" msgstr "Рекомендуемые" +#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Decrease" @@ -4783,11 +4845,12 @@ msgstr "V1 Avalanche" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Collateral ({0})" msgstr "Залог ({0})" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This order will not be valid for that position. <0>Switch to {1} collateral" msgstr "У вас есть существующая позиция с {0} в качестве обеспечения. Этот ордер не будет действителен для этой позиции. <0>Переключиться на обеспечение {1}" @@ -4890,10 +4953,6 @@ msgstr "Ошибка ордера. Цены в настоящее время в msgid "Swap Price Impact for External Swap Threshold" msgstr "Влияние цены обмена для порога внешнего обмена" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Limit / Take Profit / Stop Loss" -msgstr "Лимит / Тейк-профит / Стоп-лосс" - #: landing/src/pages/Home/LiqiuditySection/PoolCards.tsx msgid "Steady returns without management" msgstr "Стабильная доходность без управления" @@ -4906,10 +4965,6 @@ msgstr "Спред" msgid "This position was liquidated as the max. leverage of {formattedMaxLeverage} was exceeded when taking into account fees." msgstr "Эта позиция была ликвидирована, так как максимальное кредитное плечо {formattedMaxLeverage} было превышено с учетом комиссий." -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Swap {0}" -msgstr "Обмен {0}" - #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx @@ -4999,10 +5054,6 @@ msgstr "APR wstETH" msgid "Telegram Group" msgstr "Группа в Telegram" -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Long TP/SL: {0} a long position when the trigger price is reached." -msgstr "{typeString} Лонг TP/SL: {0} длинную позицию, когда достигнута цена триггера." - #: src/components/OrderItem/TwapOrdersList/TwapOrdersList.tsx msgid "<0>{fromTokenText} {fromTokenIcon}<1> to {toTokenIcon}" msgstr "<0>{fromTokenText} {fromTokenIcon}<1> в {toTokenIcon}" @@ -5031,7 +5082,7 @@ msgstr "" #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/GmxAssetCard.tsx #: src/components/MarketStats/MarketGraphs.tsx #: src/components/SwapCard/SwapCard.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TradeboxMarginFields/PriceField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TVChart/Chart.tsx #: src/pages/Dashboard/GmxCard.tsx @@ -5194,6 +5245,10 @@ msgstr "Хэш транзакции моста" msgid "Start unrealized pnl" msgstr "Начальный нереализованный PnL" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of Position)" +msgstr "" + #: src/domain/tokens/approveTokens.tsx msgid "Approval submitted! <0>View status." msgstr "Одобрение подано! <0>Просмотреть статус." @@ -5215,7 +5270,7 @@ msgid "Stake GMX to earn GMX rewards" msgstr "Стейкайте GMX, чтобы получать награды GMX" #: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionList/PositionList.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "TP/SL" msgstr "TP/SL" @@ -5443,6 +5498,7 @@ msgstr "Управление" #: src/components/OrderItem/OrderItem.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Cancel" msgstr "Отменить" @@ -5509,6 +5565,11 @@ msgstr "90д" msgid "Liquidation" msgstr "Ликвидация" +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Add TP/SL" +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Risk Monitoring" msgstr "Мониторинг рисков GMX" @@ -5553,6 +5614,7 @@ msgid "Longs Net Rate / 1h" msgstr "Чистая ставка лонгов / 1ч" #: src/components/OrderItem/OrderItem.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx msgid "Edit" msgstr "Редактировать" @@ -5565,10 +5627,6 @@ msgstr "Доступное кредитное плечо не найдено" msgid "You have a <0>pending transfer to {pendingReceiver}." msgstr "У вас есть <0>ожидающий перевода на {pendingReceiver}." -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Limit size is required" -msgstr "Требуется лимит размера" - #: src/components/GmxAccountModal/DepositView.tsx msgid "From Network" msgstr "Из сети" @@ -5585,6 +5643,10 @@ msgstr "Вывести со счета GMX" msgid "Collateral at Liquidation" msgstr "Залог при ликвидации" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Keep leverage at {currentLeverage}" +msgstr "" + #: src/lib/dates.ts #: src/pages/AccountDashboard/GeneralPerformanceDetails.tsx msgid "Today" @@ -5969,10 +6031,6 @@ msgstr "Отрицательная комиссия финансирования msgid "{fromText} to {toExecutionText}" msgstr "{fromText} в {toExecutionText}" -#: src/components/PositionSeller/PositionSeller.tsx -msgid "Create {0} Order" -msgstr "Создать {0} ордер" - #: src/context/SubaccountContext/SubaccountContextProvider.tsx msgid "Settings updated." msgstr "Настройки обновлены." @@ -6050,11 +6108,6 @@ msgstr "Обмен {fromAmount} на {toAmount}" msgid "Amount" msgstr "Сумма" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create {0} order" -msgstr "Создать {0} ордер" - #: src/components/PositionEditor/PositionEditor.tsx msgid "Edit {0} {1}{2}" msgstr "Редактировать {0} {1}{2}" @@ -6136,6 +6189,7 @@ msgstr "Возможно, не будет достаточно ликвидно #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -6242,6 +6296,7 @@ msgstr "Комиссия за продажу" #: src/components/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Execution Details" msgstr "Детали исполнения" @@ -6266,7 +6321,7 @@ msgstr "Обновить тейк-профит" msgid "Only addresses with over {0} in capital used are ranked.<0/><1/>The capital used is calculated as the highest value of [<2>sum of collateral of open positions - realized PnL + period start pending PnL]." msgstr "Только адреса с использованным капиталом более {0} включаются в рейтинг.<0/><1/>Использованный капитал рассчитывается как максимальное значение [<2>сумма обеспечения открытых позиций - реализованный PnL + ожидающий PnL на начало периода]." -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Active Orders" msgstr "Активные ордера" @@ -6317,6 +6372,10 @@ msgstr "Аналитика протокола" msgid "More active management required compared to GLV" msgstr "Требует более активного управления по сравнению с GLV" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Take Profit Price" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed amount" msgstr "Комиссии превышают сумму" @@ -6419,6 +6478,10 @@ msgstr "Kudai AI агент" msgid "Current Price" msgstr "Текущая цена" +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +msgid "Enter TP/SL price" +msgstr "" + #: src/pages/NftWallet/NftWallet.jsx msgid "Enter NFT ID" msgstr "Введите ID NFT" @@ -6465,10 +6528,6 @@ msgstr "Депозит" msgid "365d Est. Fees" msgstr "Оценка комиссий за 365д" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below highest limit price" -msgstr "Цена триггера ниже наивысшей лимитной цены" - #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/ClaimModal.tsx msgid "Select rewards to claim" msgstr "Выберите награды для получения" @@ -6536,7 +6595,9 @@ msgstr "Запросить <0>{0}" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx @@ -6581,7 +6642,9 @@ msgid "We Value Your Feedback" msgstr "Мы ценим ваш отзыв" #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Stop Loss" msgstr "Стоп-лосс" @@ -6634,6 +6697,10 @@ msgstr "Ошибка при отправке ордера" msgid "Duration" msgstr "Продолжительность" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "TP Price" +msgstr "" + #: src/components/Seo/SEO.tsx msgid "Trade spot or perpetual BTC, ETH, AVAX and other top cryptocurrencies with up to 100x leverage directly from your wallet on Arbitrum and Avalanche." msgstr "Торгуйте спот или перпетуалы BTC, ETH, AVAX и другие топ криптовалюты с плечом до 100x прямо из вашего кошелька на Arbitrum и Avalanche." @@ -6790,7 +6857,8 @@ msgstr "Награды GMX:" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderItem/OrderItem.tsx #: src/components/OrderList/OrderList.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx msgid "Trigger Price" @@ -7025,6 +7093,10 @@ msgstr "сдвиг" msgid "Why is the claim in GLV tokens?" msgstr "Почему клейм в токенах GLV?" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PNL" +msgstr "" + #: src/components/SettingsModal/TradingSettings.tsx msgid "Express + One-Click" msgstr "Express + One-Click" @@ -7091,6 +7163,8 @@ msgstr "Ошибка сдвига." #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -7109,7 +7183,7 @@ msgstr "Лонг" #: src/components/PoolSelector2/PoolSelector2.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx -#: src/components/TradeBox/MarketPoolSelectorRow.tsx +#: src/components/TradeBox/MarketPoolSelectorField.tsx msgid "Pool" msgstr "Пул" @@ -7202,6 +7276,8 @@ msgstr "SNAPSHOT" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Liquidation Price" msgstr "Цена ликвидации" @@ -7259,6 +7335,7 @@ msgstr "Подтверждение..." msgid "Deposited:" msgstr "Внесено:" +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Increase" @@ -7384,6 +7461,10 @@ msgstr "Завершено" msgid "Referral Terms" msgstr "Условия рефералов" +#: src/components/PositionDropdown/PositionDropdown.tsx +msgid "Increase Size (TWAP)" +msgstr "" + #: src/components/Earn/Portfolio/RecommendedAssets/RecommendedAssets.tsx #: src/components/GmList/FeeApyLabel.tsx msgid "Fee APY" @@ -7394,7 +7475,6 @@ msgid "Can I build on top of GMX or integrate it into my DeFi app?" msgstr "Могу ли я строить на базе GMX или интегрировать его в мое DeFi-приложение?" #: src/components/GmSwap/GmSwapBox/GmSwapWarningsRow.tsx -#: src/components/TradeBox/TradeBox.tsx msgid "Support for GMX accounts on Avalanche will be discontinued soon. Opening new positions from and depositing additional funds to Avalanche GMX accounts is no longer available. We recommend switching to Arbitrum as a settlement network." msgstr "Поддержка счетов GMX на Avalanche скоро будет прекращена. Открытие новых позиций с и пополнение счетов GMX на Avalanche больше недоступно. Мы рекомендуем переключиться на Arbitrum в качестве сети для расчетов." @@ -7437,6 +7517,10 @@ msgstr "Допускаются только буквы, цифры и подче msgid "High TWAP network fee" msgstr "Высокая сетевая комиссия TWAP" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the TP/SL button to set TP/SL orders." +msgstr "" + #: src/components/Earn/Discovery/EarnProductCard.tsx msgid "What is GM?" msgstr "Что такое GM?" @@ -7562,6 +7646,7 @@ msgstr "<0>каждые {minutes} минут{0}" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/EntryPriceRow.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Entry Price" @@ -7636,10 +7721,6 @@ msgstr "Конвертировать токены esGMX в GMX. Пожалуйс msgid "You have an existing position in the {0} market pool.<0>Switch to {1} market pool" msgstr "У вас есть существующая позиция в пуле рынка {0}.<0>Переключиться на пул рынка {1}" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Stop Loss PnL" -msgstr "PnL стоп-лосса" - #: src/components/TVChartContainer/constants.ts msgid "Market - Long Dec." msgstr "Рынок - Лонг Уменьшение" @@ -7787,6 +7868,7 @@ msgstr "Клейм средств" msgid "30d Fee APY" msgstr "Комиссионная APY за 30 дней" +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "Net Price Impact / Fees" msgstr "Чистое влияние цены / Комиссии" @@ -7858,6 +7940,7 @@ msgstr "Сгенерировать реферальный код" #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/SwapCard/SwapCard.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "Swap" msgstr "Своп" @@ -7896,10 +7979,6 @@ msgstr "Язык" msgid "Complete Transfer" msgstr "Завершить перевод" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below lowest limit price" -msgstr "Триггерная цена ниже минимальной лимитной цены" - #: src/components/Referrals/TradersStats.tsx msgid "GMX V2 discounts are automatically applied on each trade and are not displayed on this table." msgstr "Скидки GMX V2 автоматически применяются на каждой сделке и не отображаются в этой таблице." @@ -7976,10 +8055,6 @@ msgstr "Последние 30д" msgid "Dashboards" msgstr "Панели" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create TWAP {0} order" -msgstr "Создать TWAP {0} ордер" - #: src/components/AddressDropdown/AddressDropdownWithoutMultichain.tsx msgid "Address copied to your clipboard." msgstr "Адрес скопирован в буфер обмена." @@ -8006,6 +8081,10 @@ msgstr "Открыть приложение" msgid "7d" msgstr "7д" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "TP/SL fees" +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "Rage Trade" msgstr "Rage Trade" @@ -8032,6 +8111,7 @@ msgstr "Перевод не удался." #: src/components/OrderList/OrderList.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Mark Price" @@ -8153,7 +8233,7 @@ msgstr "Очень вероятно" msgid "Withdrawing funds from GMX Account..." msgstr "Вывод средств с аккаунта GMX…" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing limit order with {symbol} as collateral. <0>Switch to {symbol} collateral" msgstr "У вас есть существующий лимитный ордер с {symbol} как коллатералем. <0>Переключиться на коллатераль {symbol}" @@ -8272,7 +8352,6 @@ msgstr "Загрузка" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/TradeBox/tradeboxConstants.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx #: src/components/TradeHistory/keys.ts #: src/domain/synthetics/positions/utils.ts msgid "Limit" @@ -8324,10 +8403,9 @@ msgid "multiple asset classes" msgstr "множество классов активов" #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/multichain/progress/MultichainTransferProgressView.tsx diff --git a/src/locales/zh/messages.po b/src/locales/zh/messages.po index 9200052b06..919d1134a0 100644 --- a/src/locales/zh/messages.po +++ b/src/locales/zh/messages.po @@ -73,6 +73,11 @@ msgstr "价格" msgid "receive" msgstr "接收" +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx +msgid "Create basic TP/SL orders that fully close your position. For advanced TP/SL setup, use the positions list after opening a position." +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Referrals Dashboard" msgstr "GMX 推荐仪表板" @@ -133,7 +138,7 @@ msgstr "Avalanche 上的产量优化器" msgid "Arbitrum" msgstr "Arbitrum" -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Orders ({0})" msgstr "订单 ({0})" @@ -148,6 +153,8 @@ msgstr "止损" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/Referrals/AffiliatesStats.tsx #: src/components/Referrals/TradersStats.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx #: src/components/UserIncentiveDistribution/UserIncentiveDistribution.tsx msgid "Type" @@ -264,6 +271,10 @@ msgstr "执行市场交换" msgid "Discover" msgstr "发现" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Full Position Close" +msgstr "" + #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "There is no price impact for increase orders, orders are filled at the mark price. <0>Read more." msgstr "增仓订单没有价格影响,订单按标记价格成交。<0>了解更多。" @@ -428,7 +439,6 @@ msgstr "超过池最大 USD" #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price below mark price" @@ -641,6 +651,10 @@ msgstr "Telegram 联系方式(可选)" msgid "Market order will be cancellable in {minutesText}{seconds}s." msgstr "市场订单将在 {minutesText}{seconds}秒 内可取消。" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Edit TP/SL Size" +msgstr "" + #: src/components/UserIncentiveDistribution/AboutGlpIncident.tsx msgid "About GLP Incident" msgstr "关于 GLP 事件" @@ -675,17 +689,19 @@ msgid "Settling" msgstr "结算中" #: src/components/PositionDropdown/PositionDropdown.tsx -#: src/components/PositionItem/PositionItem.tsx msgid "Edit Collateral" msgstr "编辑抵押品" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "SL Price" +msgstr "" + #: src/components/Earn/Discovery/EarnFaq.tsx msgid "GLV and GM both enable liquidity provision on GMX, allowing users to earn fees from three revenue sources that auto-compound into the token's price. GM tokens represent liquidity in a single pool that supports one specific market on GMX. GLV vaults contain multiple GM tokens in an auto-rebalancing composition, thereby aggregating liquidity across various markets that have the same backing tokens (ex: ETH-USDC, or BTC-USDC). GM enables users to provide isolated liquidity with exposure to a specific market, while GLV allows users to passively earn yield from a diverse range of highly utilized pools." msgstr "GLV 和 GM 均可在 GMX 上提供流动性,使用户从三种收入来源赚取费用,并自动复利至代币价格中。GM 代币代表支持 GMX 上单一市场的资金池流动性。GLV 金库包含多种 GM 代币,采用自动再平衡组合,从而在具有相同底层代币的多个市场(例如 ETH-USDC 或 BTC-USDC)中聚合流动性。GM 允许用户提供针对特定市场的隔离流动性,而 GLV 则让用户被动地从多样化、高利用率资金池中赚取收益。" #: src/components/OrderEditor/OrderEditor.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Limit price above mark price" @@ -862,6 +878,10 @@ msgstr "解除质押 {tokenSymbol}" msgid "MAX" msgstr "最大" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Stop Loss Price" +msgstr "" + #: src/pages/BuyGMX/BuyGMX.tsx msgid "No centralized exchanges available for this network." msgstr "此网络没有可用的中心化交易所。" @@ -929,6 +949,8 @@ msgstr "您即将导航到 GMX Solana,该平台由单独的团队运营,因 #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +#: src/components/TradeboxMarginFields/MarginToPayField.tsx msgid "Pay" msgstr "支付" @@ -1111,6 +1133,7 @@ msgstr "允许将我所有的代币转移到新账户" #: src/components/OrderItem/OrderItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Collateral" msgstr "抵押品" @@ -1316,6 +1339,10 @@ msgstr "最小订单:{0}" msgid "Wallet is not connected" msgstr "钱包未连接" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of position)" +msgstr "" + #: landing/src/pages/Home/HeroSection/AnimatedTitle.tsx msgid "100+ crypto tokens" msgstr "超过 100 种加密代币" @@ -1338,6 +1365,10 @@ msgstr "GM 是 GMX V2 市场的流动性提供者代币。累积 V2 市场生成 msgid "The current price impact is {0}. Consider adding a buffer of 0.30% to it so the order is more likely to be processed." msgstr "当前价格影响为 {0}。考虑添加 0.30% 的缓冲,使订单更可能被处理。" +#: src/components/TPSLModal/TPSLModal.tsx +msgid "TP/SL: {positionTitle}" +msgstr "" + #: src/components/SettingsModal/RpcDebugSettings.tsx msgid "Public" msgstr "公开" @@ -1444,6 +1475,10 @@ msgstr "时间戳" msgid "The amount you are trying to withdraw exceeds the limit. Please try an amount smaller than <0>{upperLimitFormatted}." msgstr "您尝试提取的金额超过限制。请尝试小于 <0>{upperLimitFormatted} 的金额。" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Gain" +msgstr "" + #: src/components/NotifyModal/NotifyModal.tsx msgid "Discover GMX Alerts" msgstr "发现 GMX 警报" @@ -1668,10 +1703,6 @@ msgstr "未找到交换路径" msgid "RPnL" msgstr "已实现盈亏" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined stop losses are at maximum (100%). Decrease existing values to add more orders." -msgstr "组合止损已达到最大 (100%)。减少现有值以添加更多订单。" - #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx #: src/pages/LeaderboardPage/components/CompetitionPrizes.tsx msgid "4-18 Places" @@ -1768,7 +1799,6 @@ msgid "Min size per part: {0}" msgstr "每个部分最小规模:{0}" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price above liq. price" msgstr "触发价格高于清算价格" @@ -1800,7 +1830,6 @@ msgstr "基于期权的金库" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionSeller/PositionSeller.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Mark" @@ -1942,6 +1971,7 @@ msgstr "费用的 USD 值在赚取时计算,不包括激励。" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeboxMarginFields/MarginPercentageSlider.tsx msgid "Max" msgstr "最大" @@ -2020,9 +2050,9 @@ msgstr "在 {chainName} 上购买 GMX" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx +#: src/components/TradeboxMarginFields/SizeField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -2174,6 +2204,7 @@ msgstr "当前端点 ({0})" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Initial collateral (collateral excluding borrow and funding fee)." msgstr "初始抵押品(排除借款和资金费用的抵押品)。" @@ -2435,10 +2466,6 @@ msgstr "桥接和交换" msgid "The execution price may vary from your set limit price due to fees and price impact, ensuring you receive the displayed minimum receive amount. <0>Read more." msgstr "由于费用和价格影响,执行价格可能与您设置的限价不同,但确保您收到显示的最低接收金额。<0>了解更多。" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above lowest limit price" -msgstr "触发价格高于最低限价" - #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2528,6 +2555,10 @@ msgstr "年化收益率每周三更新,具体取决于当周收取的费用。 msgid "Fees (Incl. Swap)" msgstr "费用(包括交换)" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "Order update fee" +msgstr "" + #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts #: src/components/TradeHistory/TradeHistoryRow/utils/swap.ts msgid "{fromText} to {toMinText}" @@ -2614,6 +2645,7 @@ msgid "Exposure to Backing Tokens" msgstr "对支撑代币的敞口" #: src/components/Referrals/AddAffiliateCode.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx msgid "Creating..." msgstr "正在创建…" @@ -2669,7 +2701,9 @@ msgstr "以其他代币:" msgid "Returned Collateral" msgstr "返回抵押品" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "There are issues in the TP/SL orders." msgstr "TP/SL 订单存在问题。" @@ -2762,10 +2796,6 @@ msgstr "从中心化交易所购买 GMX:" msgid "GMX bonds can be bought on Bond Protocol with a discount and a small vesting period:" msgstr "GMX 债券可在 Bond Protocol 上以折扣和小授权期购买:" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price above highest limit price" -msgstr "触发价格高于最高限价" - #: src/components/Earn/Discovery/EarnFaq.tsx msgid "Yes, staking and providing liquidity are fully permissionless. The GMX, GLV, and GM tokens are liquid and can be unstaked and withdrawn at any time. For LP tokens, there could be some rare edge cases where your liquidity is being used for active trades. In that case, you can withdraw as soon as positions close." msgstr "是的,质押和提供流动性完全无需许可。GMX、GLV 和 GM 代币均具有流动性,可随时解除质押并提取。对于 LP 代币,极少数情况下您的流动性可能正被用于活跃交易,此时需等待仓位关闭后即可提取。" @@ -2895,12 +2925,16 @@ msgstr "<0>出售 {0}{1}<1>{poolName}" msgid "No eligible tokens available on {0} for deposit" msgstr "在 {0} 上没有符合条件的代币可存入" +#: src/components/TPSLModal/TPSLInputRow.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PnL" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Max close amount exceeded" msgstr "最大关闭金额超出" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -2986,6 +3020,7 @@ msgstr "20 秒" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx @@ -3025,7 +3060,8 @@ msgstr "可用流动性不足。当有流动性可用时,订单将成交。" #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Claims/ClaimsHistory.tsx #: src/components/OrderItem/OrderItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/Referrals/ClaimAffiliatesModal/ClaimAffiliatesModal.tsx #: src/components/TableMarketFilter/MarketFilterBase.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx @@ -3114,7 +3150,6 @@ msgid "Debug values are not available" msgstr "调试值不可用" #: src/domain/synthetics/orders/getPositionOrderError.tsx -#: src/domain/synthetics/sidecarOrders/utils.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Trigger price below liq. price" msgstr "触发价格低于清算价格" @@ -3247,10 +3282,6 @@ msgstr "平均规模" msgid "V1 esGMX" msgstr "V1 esGMX" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Take Profit PnL" -msgstr "止盈 PnL" - #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your GLV or GM tokens, and borrow against them, or put your LP tokens into strategies to maximize your yield." msgstr "借出您的 GLV 或 GM 代币并以此抵押借款,或将 LP 代币投入策略以最大化收益。" @@ -3295,6 +3326,7 @@ msgstr "领取中..." #: src/components/Earn/AdditionalOpportunities/OpportunityFilters.tsx #: src/components/GmxAccountModal/AvailableToTradeAssetsView.tsx #: src/components/TokenSelector/MultichainMarketTokenSelector.tsx +#: src/components/TPSLModal/TPSLModal.tsx msgid "All" msgstr "全部" @@ -3321,7 +3353,8 @@ msgstr "{longOrShort} 仓位 {fundingAction} 每小时 {fundingRate} 的资金 #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItem.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/domain/synthetics/positions/utils.ts msgid "TWAP" @@ -3401,10 +3434,6 @@ msgstr "允许滑点" msgid "{longOrShort} positions {fundingAction} a funding fee of {fundingRate} per hour and do not pay a borrow fee." msgstr "{longOrShort} 仓位 {fundingAction} 每小时 {fundingRate} 的资金费用,且不支付借贷费用。" -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Short TP/SL: {0} a short position when the trigger price is reached." -msgstr "{typeString} 做空 TP/SL:当达到触发价格时 {0} 一个做空仓位。" - #: src/pages/Ecosystem/Ecosystem.tsx msgid "Community-led Telegram groups." msgstr "社区主导的 Telegram 群组。" @@ -3438,6 +3467,10 @@ msgstr "请求市场交换" msgid "Insufficient liquidity in the {0} market pool. Select a different pool for this market.{1}" msgstr "{0} 市场池流动性不足。请为此市场选择不同的池。{1}" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Loss" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "App disabled, pending {0} upgrade" msgstr "应用程序禁用,待 {0} 升级" @@ -3472,6 +3505,10 @@ msgstr "申领资金费用成功" msgid "<0>every {hours} hours{0}" msgstr "<0>每 {hours} 小时{0}" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "{secondLabel}" +msgstr "" + #: src/components/DebugExpressSettings/DebugSwapsSettings.tsx #: src/components/DebugSwapsSettings/DebugSwapsSettings.tsx msgid "Fail External Swaps" @@ -3501,13 +3538,17 @@ msgstr "交换已提交。" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/pages/AccountDashboard/DailyAndCumulativePnL.tsx msgid "PnL" msgstr "PnL" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "No TP/SL orders" +msgstr "" + #: src/components/TradeBox/ExpressTradingWarningCard.tsx msgid "Express Trading is not available using network's native token {0}. Consider using {1} instead." msgstr "使用网络的本机代币 {0} 无法进行快速交易。请考虑使用 {1}。" @@ -3773,7 +3814,6 @@ msgstr "赚取" #: src/components/CollateralSelector/CollateralSelector.tsx #: src/components/CollateralSelector/PositionEditorCollateralSelector.tsx -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx msgid "Collateral In" msgstr "抵押品进入" @@ -3785,6 +3825,10 @@ msgstr "GM 市场" msgid "Your opinions and experiences matter to us. Your feedback helps us understand what we are doing well and where we can make enhancements." msgstr "您的意见和经验对我们很重要。您的反馈帮助我们了解我们做得好以及哪里可以改进。" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Create TP/SL" +msgstr "" + #: src/components/GmxAccountModal/DepositView.tsx #: src/components/GmxAccountModal/WithdrawalView.tsx msgid "Estimated Time" @@ -3823,6 +3867,10 @@ msgstr "该账户作为交易者赚取的回扣。" msgid "Transfer account" msgstr "转账账户" +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Cancel all" +msgstr "" + #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx #: src/components/Earn/AdditionalOpportunities/useOpportunities.tsx msgid "Lend out your tokens, or borrow against it." @@ -3845,8 +3893,6 @@ msgstr "<0>GMX 与动态平衡的流动性池执行交易,与传统订单簿 #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx msgid "Read more" msgstr "阅读更多" @@ -3915,6 +3961,7 @@ msgstr "探索更多" #: src/components/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/AllowedSwapSlippageInputRowImpl/AllowedSwapSlippageInputRowImpl.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx #: src/pages/LeaderboardPage/components/LeaderboardAccountsTable.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx @@ -3941,6 +3988,8 @@ msgstr "订单的触发价格。" #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -4009,10 +4058,6 @@ msgstr "交易对价格警报" msgid "Links" msgstr "链接" -#: src/components/PositionItem/PositionItem.tsx -msgid "Use the \"Close\" button to reduce your position via market, TP/SL, or TWAP orders." -msgstr "使用「平仓」按钮通过市价、止盈/止损或 TWAP 订单减少您的仓位。" - #: src/components/Seo/SEO.tsx msgid "GMX | Decentralized Perpetual Exchange" msgstr "GMX | 去中心化永续交易所" @@ -4227,7 +4272,7 @@ msgstr "职位" msgid "Decentralized Options Strategies" msgstr "去中心化期权策略" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This action will not apply for that position. <0>Switch to {1} collateral" msgstr "您有一个现有仓位以 {0} 作为抵押品。此操作不适用于该仓位。<0>切换到 {1} 抵押品" @@ -4267,7 +4312,9 @@ msgid "Amount of traders you referred." msgstr "您推荐的交易者数量。" #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Take Profit" msgstr "止盈" @@ -4285,6 +4332,14 @@ msgstr "{typeString} 多头止损市价单:当价格低于触发价格时{0} msgid "Insufficient {0} liquidity" msgstr "{0} 流动性不足" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Unable to calculate order" +msgstr "" + +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "TP/SL: {positionTitle} Decrease" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.tsx #: src/pages/Ecosystem/Ecosystem.tsx msgid "Creator" @@ -4310,6 +4365,10 @@ msgstr "创建限价" msgid "Transfer already initiated" msgstr "转账已启动" +#: src/components/TradeboxMarginFields/MarginToPayField.tsx +msgid "Margin to Pay" +msgstr "" + #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx msgid "Withdraw submitted." msgstr "提取已提交。" @@ -4387,7 +4446,8 @@ msgstr "Botanix" msgid "Accrued price impact rebates. They will become claimable after 5 days.<0/><1/><2>Read more." msgstr "累积的价格影响返利。5 天后可申领。<0/><1/><2>阅读更多。" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/OrderEditor/OrderEditor.tsx +#: src/components/TradeBox/TradeBoxRows/TPSLRows.tsx msgid "Take Profit / Stop Loss" msgstr "止盈 / 止损" @@ -4483,6 +4543,10 @@ msgstr "未找到市场。" msgid "RPC Monitoring" msgstr "RPC 监控" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the \"Close\" button to reduce your position via market or TWAP orders." +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "ODOS" msgstr "ODOS" @@ -4544,10 +4608,6 @@ msgstr "高价格影响" msgid "<0>Solana Support<1>Botanix Support<2>GMX Express" msgstr "<0>Solana 支持<1>Botanix 支持<2>GMX 快速" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Combined take profits are at maximum (100%). Decrease existing values to add more orders." -msgstr "组合止盈已达到最大 (100%)。减少现有值以添加更多订单。" - #: src/components/GmList/GmList.tsx msgid "POOL" msgstr "池" @@ -4580,6 +4640,8 @@ msgstr "正在卖出" msgid "Recommended" msgstr "推荐" +#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Decrease" @@ -4783,11 +4845,12 @@ msgstr "V1 Avalanche" #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Collateral ({0})" msgstr "抵押品 ({0})" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing position with {0} as collateral. This order will not be valid for that position. <0>Switch to {1} collateral" msgstr "您有一个以 {0} 作为抵押品的现有仓位。此订单对该仓位无效。<0>切换到 {1} 抵押品" @@ -4890,10 +4953,6 @@ msgstr "订单错误。此市场价格当前波动较大,请通过 <0>增加 msgid "Swap Price Impact for External Swap Threshold" msgstr "外部交换的交换价格影响阈值" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Limit / Take Profit / Stop Loss" -msgstr "限价 / 止盈 / 止损" - #: landing/src/pages/Home/LiqiuditySection/PoolCards.tsx msgid "Steady returns without management" msgstr "无需管理的稳定回报" @@ -4906,10 +4965,6 @@ msgstr "价差" msgid "This position was liquidated as the max. leverage of {formattedMaxLeverage} was exceeded when taking into account fees." msgstr "由于在考虑费用时超出了 {formattedMaxLeverage} 的最大杠杆,该头寸已被清算。" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Swap {0}" -msgstr "交换 {0}" - #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx #: src/App/MainRoutes.tsx @@ -4999,10 +5054,6 @@ msgstr "wstETH APR" msgid "Telegram Group" msgstr "Telegram 群组" -#: src/components/TradeInfoIcon/TradeInfoIcon.tsx -msgid "{typeString} Long TP/SL: {0} a long position when the trigger price is reached." -msgstr "{typeString} 多头 TP/SL:{0} 当达到触发价格时开立多头头寸。" - #: src/components/OrderItem/TwapOrdersList/TwapOrdersList.tsx msgid "<0>{fromTokenText} {fromTokenIcon}<1> to {toTokenIcon}" msgstr "<0>{fromTokenText} {fromTokenIcon}<1> 到 {toTokenIcon}" @@ -5031,7 +5082,7 @@ msgstr "" #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/GmxAssetCard.tsx #: src/components/MarketStats/MarketGraphs.tsx #: src/components/SwapCard/SwapCard.tsx -#: src/components/TradeBox/components/SideOrderEntries.tsx +#: src/components/TradeboxMarginFields/PriceField.tsx #: src/components/TradeHistory/TradeHistory.tsx #: src/components/TVChart/Chart.tsx #: src/pages/Dashboard/GmxCard.tsx @@ -5194,6 +5245,10 @@ msgstr "跨链交易哈希" msgid "Start unrealized pnl" msgstr "起始未实现 PnL" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Size (% of Position)" +msgstr "" + #: src/domain/tokens/approveTokens.tsx msgid "Approval submitted! <0>View status." msgstr "批准已提交!<0>查看状态。" @@ -5215,7 +5270,7 @@ msgid "Stake GMX to earn GMX rewards" msgstr "质押 GMX 赚取 GMX 奖励" #: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/PositionList/PositionList.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "TP/SL" msgstr "TP/SL" @@ -5443,6 +5498,7 @@ msgstr "治理" #: src/components/OrderItem/OrderItem.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Cancel" msgstr "取消" @@ -5509,6 +5565,11 @@ msgstr "90 天" msgid "Liquidation" msgstr "清算" +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +msgid "Add TP/SL" +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "GMX Risk Monitoring" msgstr "GMX 风险监控" @@ -5553,6 +5614,7 @@ msgid "Longs Net Rate / 1h" msgstr "多头净率 / 1 小时" #: src/components/OrderItem/OrderItem.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx msgid "Edit" msgstr "编辑" @@ -5565,10 +5627,6 @@ msgstr "未找到可用杠杆" msgid "You have a <0>pending transfer to {pendingReceiver}." msgstr "您有一笔<0>待处理转账给 {pendingReceiver}。" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Limit size is required" -msgstr "需要限价大小" - #: src/components/GmxAccountModal/DepositView.tsx msgid "From Network" msgstr "从网络" @@ -5585,6 +5643,10 @@ msgstr "从 GMX 账户提取" msgid "Collateral at Liquidation" msgstr "清算时的抵押品" +#: src/components/TPSLModal/AddTPSLModal.tsx +msgid "Keep leverage at {currentLeverage}" +msgstr "" + #: src/lib/dates.ts #: src/pages/AccountDashboard/GeneralPerformanceDetails.tsx msgid "Today" @@ -5969,10 +6031,6 @@ msgstr "负资金费用" msgid "{fromText} to {toExecutionText}" msgstr "{fromText} 到 {toExecutionText}" -#: src/components/PositionSeller/PositionSeller.tsx -msgid "Create {0} Order" -msgstr "创建 {0} 订单" - #: src/context/SubaccountContext/SubaccountContextProvider.tsx msgid "Settings updated." msgstr "设置已更新。" @@ -6050,11 +6108,6 @@ msgstr "将 {fromAmount} 交换为 {toAmount}" msgid "Amount" msgstr "金额" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create {0} order" -msgstr "创建 {0} 订单" - #: src/components/PositionEditor/PositionEditor.tsx msgid "Edit {0} {1}{2}" msgstr "编辑 {0} {1}{2}" @@ -6136,6 +6189,7 @@ msgstr "当满足最低接收时,可能没有足够的流动性来执行您的 #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/StakeModal.tsx #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/VestModal.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/domain/synthetics/orders/getPositionOrderError.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts @@ -6242,6 +6296,7 @@ msgstr "出售费用" #: src/components/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/PositionEditor/PositionEditorAdvancedRows.tsx #: src/components/PositionSeller/PositionSellerAdvancedDisplayRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/AdvancedDisplayRows.tsx msgid "Execution Details" msgstr "执行细节" @@ -6266,7 +6321,7 @@ msgstr "更新止盈" msgid "Only addresses with over {0} in capital used are ranked.<0/><1/>The capital used is calculated as the highest value of [<2>sum of collateral of open positions - realized PnL + period start pending PnL]." msgstr "只有使用资金超过 {0} 的地址才会被排名。<0/><1/>使用资金计算为 [<2>未平仓仓位的抵押品总和 - 已实现 PnL + 期初待定 PnL] 的最高值。" -#: src/components/PositionItem/PositionItem.tsx +#: src/components/PositionItem/PositionItemOrders.tsx msgid "Active Orders" msgstr "活跃订单" @@ -6317,6 +6372,10 @@ msgstr "协议分析" msgid "More active management required compared to GLV" msgstr "相较于 GLV 需要更多主动管理" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "Take Profit Price" +msgstr "" + #: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed amount" msgstr "费用超过金额" @@ -6419,6 +6478,10 @@ msgstr "Kudai AI 代理" msgid "Current Price" msgstr "当前价格" +#: src/components/OrderEditor/hooks/useOrderEditorTPSL.ts +msgid "Enter TP/SL price" +msgstr "" + #: src/pages/NftWallet/NftWallet.jsx msgid "Enter NFT ID" msgstr "输入 NFT ID" @@ -6465,10 +6528,6 @@ msgstr "存入" msgid "365d Est. Fees" msgstr "365 天预估费用" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below highest limit price" -msgstr "触发价格低于最高限价" - #: src/components/Earn/Portfolio/AssetsList/GmxAssetCard/ClaimModal.tsx msgid "Select rewards to claim" msgstr "选择要领取的奖励" @@ -6536,7 +6595,9 @@ msgstr "领取 <0>{0}" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeBox/TradeBox.tsx @@ -6581,7 +6642,9 @@ msgid "We Value Your Feedback" msgstr "我们重视您的反馈" #: src/components/OrderList/filters/OrderTypeFilter.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/domain/synthetics/positions/utils.ts msgid "Stop Loss" msgstr "止损" @@ -6634,6 +6697,10 @@ msgstr "提交订单错误" msgid "Duration" msgstr "持续时间" +#: src/components/TPSLModal/TPSLInputRow.tsx +msgid "TP Price" +msgstr "" + #: src/components/Seo/SEO.tsx msgid "Trade spot or perpetual BTC, ETH, AVAX and other top cryptocurrencies with up to 100x leverage directly from your wallet on Arbitrum and Avalanche." msgstr "在 Arbitrum 和 Avalanche 上直接从您的钱包交易现货或永续 BTC、ETH、AVAX 和其他顶级加密货币,杠杆高达 100x。" @@ -6790,7 +6857,8 @@ msgstr "GMX 奖励:" #: src/components/OrderEditor/OrderEditor.tsx #: src/components/OrderItem/OrderItem.tsx #: src/components/OrderList/OrderList.tsx -#: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx +#: src/components/TPSLModal/TPSLOrdersList.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx msgid "Trigger Price" @@ -7025,6 +7093,10 @@ msgstr "转移" msgid "Why is the claim in GLV tokens?" msgstr "为什么申领以 GLV 代币形式?" +#: src/components/TPSLModal/TPSLOrdersList.tsx +msgid "Est. PNL" +msgstr "" + #: src/components/SettingsModal/TradingSettings.tsx msgid "Express + One-Click" msgstr "快速 + 一键" @@ -7091,6 +7163,8 @@ msgstr "转移错误。" #: src/components/StatusNotification/FeesSettlementStatusNotification.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/TableMarketFilter/MarketFilterLongShort.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/tradeboxConstants.tsx #: src/components/TradeHistory/TradeHistoryRow/utils/position.ts #: src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -7109,7 +7183,7 @@ msgstr "做多" #: src/components/PoolSelector2/PoolSelector2.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionItem/PositionItem.tsx -#: src/components/TradeBox/MarketPoolSelectorRow.tsx +#: src/components/TradeBox/MarketPoolSelectorField.tsx msgid "Pool" msgstr "池子" @@ -7202,6 +7276,8 @@ msgstr "快照" #: src/components/PositionEditor/PositionEditor.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx msgid "Liquidation Price" msgstr "清算价格" @@ -7259,6 +7335,7 @@ msgstr "确认中..." msgid "Deposited:" msgstr "已存入:" +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/synthetics/orders/utils.tsx msgid "Increase" @@ -7384,6 +7461,10 @@ msgstr "已完成" msgid "Referral Terms" msgstr "推荐条款" +#: src/components/PositionDropdown/PositionDropdown.tsx +msgid "Increase Size (TWAP)" +msgstr "" + #: src/components/Earn/Portfolio/RecommendedAssets/RecommendedAssets.tsx #: src/components/GmList/FeeApyLabel.tsx msgid "Fee APY" @@ -7394,7 +7475,6 @@ msgid "Can I build on top of GMX or integrate it into my DeFi app?" msgstr "我可以在 GMX 上构建或将其集成到我的 DeFi 应用中吗?" #: src/components/GmSwap/GmSwapBox/GmSwapWarningsRow.tsx -#: src/components/TradeBox/TradeBox.tsx msgid "Support for GMX accounts on Avalanche will be discontinued soon. Opening new positions from and depositing additional funds to Avalanche GMX accounts is no longer available. We recommend switching to Arbitrum as a settlement network." msgstr "Avalanche上的GMX账户支持将很快停止。从Avalanche GMX账户开立新头寸和存入额外资金的功能不再可用。我们建议切换到Arbitrum作为结算网络。" @@ -7437,6 +7517,10 @@ msgstr "仅允许字母、数字和下划线。" msgid "High TWAP network fee" msgstr "高 TWAP 网络费用" +#: src/components/PositionItem/PositionItem.tsx +msgid "Use the TP/SL button to set TP/SL orders." +msgstr "" + #: src/components/Earn/Discovery/EarnProductCard.tsx msgid "What is GM?" msgstr "什么是 GM?" @@ -7562,6 +7646,7 @@ msgstr "<0>每 {minutes} 分钟{0}" #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/EntryPriceRow.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Entry Price" @@ -7636,10 +7721,6 @@ msgstr "将 esGMX 代币转换为 GMX 代币。请在使用金库前仔细阅读 msgid "You have an existing position in the {0} market pool.<0>Switch to {1} market pool" msgstr "您在 {0} 市场资金池中有现有仓位。<0>切换到 {1} 市场资金池" -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx -msgid "Stop Loss PnL" -msgstr "止损盈亏" - #: src/components/TVChartContainer/constants.ts msgid "Market - Long Dec." msgstr "市场 - 多头减少" @@ -7787,6 +7868,7 @@ msgstr "申领资金" msgid "30d Fee APY" msgstr "30 天费用年化收益率" +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBoxRows/PriceImpactFeesRow.tsx msgid "Net Price Impact / Fees" msgstr "净价格影响 / 费用" @@ -7858,6 +7940,7 @@ msgstr "生成推荐代码" #: src/components/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/StatusNotification/OrderStatusNotification.tsx #: src/components/SwapCard/SwapCard.tsx +#: src/components/TradeBox/hooks/useTradeButtonState.tsx #: src/components/TradeBox/tradeboxConstants.tsx msgid "Swap" msgstr "交换" @@ -7896,10 +7979,6 @@ msgstr "语言" msgid "Complete Transfer" msgstr "完成转账" -#: src/domain/synthetics/sidecarOrders/utils.ts -msgid "Trigger price below lowest limit price" -msgstr "触发价格低于最低限价" - #: src/components/Referrals/TradersStats.tsx msgid "GMX V2 discounts are automatically applied on each trade and are not displayed on this table." msgstr "GMX V2 折扣自动应用于每笔交易,并未在此表格中显示。" @@ -7976,10 +8055,6 @@ msgstr "过去 30 天" msgid "Dashboards" msgstr "仪表板" -#: src/components/TradeBox/hooks/useTradeButtonState.tsx -msgid "Create TWAP {0} order" -msgstr "创建 TWAP {0} 订单" - #: src/components/AddressDropdown/AddressDropdownWithoutMultichain.tsx msgid "Address copied to your clipboard." msgstr "地址已复制到您的剪贴板。" @@ -8006,6 +8081,10 @@ msgstr "打开应用" msgid "7d" msgstr "7 天" +#: src/components/OrderEditor/OrderEditor.tsx +msgid "TP/SL fees" +msgstr "" + #: src/pages/Ecosystem/ecosystemConstants.tsx msgid "Rage Trade" msgstr "Rage Trade" @@ -8032,6 +8111,7 @@ msgstr "转移失败。" #: src/components/OrderList/OrderList.tsx #: src/components/PositionItem/PositionItem.tsx #: src/components/PositionList/PositionList.tsx +#: src/components/TPSLModal/TPSLModal.tsx #: src/components/TradeHistory/useDownloadAsCsv.tsx #: src/pages/LeaderboardPage/components/LeaderboardPositionsTable.tsx msgid "Mark Price" @@ -8153,7 +8233,7 @@ msgstr "很可能" msgid "Withdrawing funds from GMX Account..." msgstr "正在从 GMX 账户提取资金…" -#: src/components/TradeBox/TradeBoxRows/CollateralSelectorRow.tsx +#: src/components/TradeBox/TradeBoxRows/CollateralSelectorField.tsx msgid "You have an existing limit order with {symbol} as collateral. <0>Switch to {symbol} collateral" msgstr "您有一个现有限价订单以 {symbol} 作为抵押品。<0>切换到 {symbol} 抵押品" @@ -8272,7 +8352,6 @@ msgstr "加载中" #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/OrderList/filters/OrderTypeFilter.tsx #: src/components/TradeBox/tradeboxConstants.tsx -#: src/components/TradeBox/TradeBoxRows/LimitAndTPSLRows.tsx #: src/components/TradeHistory/keys.ts #: src/domain/synthetics/positions/utils.ts msgid "Limit" @@ -8324,10 +8403,9 @@ msgid "multiple asset classes" msgstr "多个资产类别" #: src/components/OrderEditor/OrderEditor.tsx -#: src/components/PositionItem/PositionItem.tsx -#: src/components/PositionItem/PositionItem.tsx #: src/components/PositionSeller/PositionSeller.tsx #: src/components/PositionSeller/PositionSeller.tsx +#: src/components/TPSLModal/AddTPSLModal.tsx #: src/components/TradeBox/TradeBox.tsx #: src/components/TradeInfoIcon/TradeInfoIcon.tsx #: src/domain/multichain/progress/MultichainTransferProgressView.tsx diff --git a/src/pages/SyntheticsPage/SyntheticsPage.tsx b/src/pages/SyntheticsPage/SyntheticsPage.tsx index 8f4047ca2a..6773b72458 100644 --- a/src/pages/SyntheticsPage/SyntheticsPage.tsx +++ b/src/pages/SyntheticsPage/SyntheticsPage.tsx @@ -37,6 +37,7 @@ import type { OrderInfo } from "domain/synthetics/orders/types"; import { useOrderTxnCallbacks } from "domain/synthetics/orders/useOrderTxnCallbacks"; import { useSetOrdersAutoCancelByQueryParams } from "domain/synthetics/orders/useSetOrdersAutoCancelByQueryParams"; import { TradeMode } from "domain/synthetics/trade"; +import { OrderOption } from "domain/synthetics/trade/usePositionSellerState"; import { useTradeParamsProcessor } from "domain/synthetics/trade/useTradeParamsProcessor"; import { useShareSuccessClosedPosition } from "domain/synthetics/tradeHistory/useShareSuccessClosedPosition"; import { useInterviewNotification } from "domain/synthetics/userFeedback/useInterviewNotification"; @@ -123,7 +124,8 @@ export function SyntheticsPage(p: Props) { const [, setClosingPositionKeyRaw] = useClosingPositionKeyState(); const setClosingPositionKey = useCallback( - (key: string | undefined) => requestAnimationFrame(() => setClosingPositionKeyRaw(key)), + (key: string | undefined, orderOption?: OrderOption) => + requestAnimationFrame(() => setClosingPositionKeyRaw(key, orderOption)), [setClosingPositionKeyRaw] );