From 4d78b7ee946ba41af69216c9a040d68e05848a8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hub=C3=A9rt=20de=20Lalye?= Date: Fri, 19 Jul 2024 13:27:11 +0400 Subject: [PATCH 01/62] adding qa-attrs --- src/components/Button/Button.tsx | 7 ++-- src/components/Button/ButtonLink.tsx | 6 ++-- .../BuyInputSection/BuyInputSection.tsx | 5 ++- src/components/Common/ConnectWalletButton.tsx | 2 +- src/components/EventToast/EventToast.tsx | 4 +-- src/components/Header/AppHeaderLinks.tsx | 19 +++++++---- src/components/Header/AppHeaderUser.tsx | 6 ++-- src/components/Header/Header.tsx | 2 +- src/components/Header/HeaderLink.tsx | 3 ++ .../LeverageSlider/LeverageSlider.tsx | 4 ++- .../MarketSelector/MarketSelector.tsx | 6 ++-- src/components/Modal/Modal.tsx | 4 ++- src/components/NumberInput/NumberInput.tsx | 4 ++- src/components/SearchInput/SearchInput.tsx | 12 ++++++- src/components/Synthetics/GmList/GmList.tsx | 34 +++++++++---------- .../Synthetics/PositionItem/PositionItem.tsx | 3 +- .../Synthetics/TradeBox/TradeBox.tsx | 22 ++++++++---- src/components/Tab/Tab.tsx | 12 +++++-- src/components/TokenIcon/TokenIcon.tsx | 9 ++++- .../TokenSelector/TokenSelector.tsx | 8 +++-- src/pages/SyntheticsPage/SyntheticsPage.tsx | 1 + 21 files changed, 118 insertions(+), 55 deletions(-) diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 04f1f44a95..7f89c60332 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -23,6 +23,7 @@ type ButtonProps = HTMLProps & { showExternalLinkArrow?: boolean; buttonRef?: RefObject; slim?: boolean; + qa?: string; }; export default function Button({ @@ -41,6 +42,7 @@ export default function Button({ buttonRef, showExternalLinkArrow: showExternalLinkArrowOverride, slim, + qa, ...rest }: ButtonProps) { const classNames = cx("button", variant, className, textAlign, { slim }); @@ -74,6 +76,7 @@ export default function Button({ showExternalLinkArrow={showExternalLinkArrow} disabled={disabled} ref={buttonRef} + qa={qa} {...rest} > {img} @@ -84,7 +87,7 @@ export default function Button({ if (onClick) { return ( - @@ -92,7 +95,7 @@ export default function Button({ } return ( - diff --git a/src/components/Button/ButtonLink.tsx b/src/components/Button/ButtonLink.tsx index 397ff84461..4eb59d84f6 100644 --- a/src/components/Button/ButtonLink.tsx +++ b/src/components/Button/ButtonLink.tsx @@ -11,6 +11,7 @@ type ButtonProps = { onClick?: (event: React.MouseEvent) => void; newTab?: boolean; disabled?: boolean; + qa?: string; }; export default function ButtonLink({ @@ -21,6 +22,7 @@ export default function ButtonLink({ showExternalLinkArrow, newTab = false, disabled = false, + qa, ...rest }: ButtonProps) { const classNames = cx(className, { disabled: disabled }); @@ -38,14 +40,14 @@ export default function ButtonLink({ : {}), }; return ( - + {showExternalLinkArrow && open in new tab} {children} ); } return ( - + {children} ); diff --git a/src/components/BuyInputSection/BuyInputSection.tsx b/src/components/BuyInputSection/BuyInputSection.tsx index 2bdb049732..e9a40011dd 100644 --- a/src/components/BuyInputSection/BuyInputSection.tsx +++ b/src/components/BuyInputSection/BuyInputSection.tsx @@ -22,6 +22,7 @@ type Props = { showPercentSelector?: boolean; onPercentChange?: (percentage: number) => void; preventFocusOnLabelClick?: "left" | "right" | "both"; + qa?: string; }; export default function BuyInputSection(props: Props) { @@ -42,6 +43,7 @@ export default function BuyInputSection(props: Props) { showPercentSelector, onPercentChange, preventFocusOnLabelClick, + qa, } = props; const [isPercentSelectorVisible, setIsPercentSelectorVisible] = useState(false); const inputRef = useRef(null); @@ -80,7 +82,7 @@ export default function BuyInputSection(props: Props) { } return ( -
+
@@ -111,6 +113,7 @@ export default function BuyInputSection(props: Props) { onFocus={handleOnFocus} onBlur={handleOnBlur} placeholder="0.0" + qa={qa ? qa + "-input" : undefined} /> )} {staticInput &&
{inputValue}
} diff --git a/src/components/Common/ConnectWalletButton.tsx b/src/components/Common/ConnectWalletButton.tsx index 24dd943c5f..105cca9a78 100644 --- a/src/components/Common/ConnectWalletButton.tsx +++ b/src/components/Common/ConnectWalletButton.tsx @@ -9,7 +9,7 @@ type Props = { export default function ConnectWalletButton({ imgSrc, children, onClick }: Props) { return ( - diff --git a/src/components/EventToast/EventToast.tsx b/src/components/EventToast/EventToast.tsx index c91df5184d..9cfa59bf60 100644 --- a/src/components/EventToast/EventToast.tsx +++ b/src/components/EventToast/EventToast.tsx @@ -17,13 +17,13 @@ export default function EventToast({ toast: Toast; }) { return ( -
+

{event.title}

- +
{Array.isArray(event.bodyText) ? ( event.bodyText.map((text, i) => diff --git a/src/components/Header/AppHeaderLinks.tsx b/src/components/Header/AppHeaderLinks.tsx index 2526ec9845..a85170159c 100644 --- a/src/components/Header/AppHeaderLinks.tsx +++ b/src/components/Header/AppHeaderLinks.tsx @@ -40,32 +40,37 @@ export function AppHeaderLinks({ small, openSettings, clickCloseIcon, showRedire
)}
- + Dashboard
- + Earn
- + Buy
- + Referrals
- + Leaderboard
- + Ecosystem
@@ -84,7 +89,7 @@ export function AppHeaderLinks({ small, openSettings, clickCloseIcon, showRedire {small && !isHomeSite() && (
{/* eslint-disable-next-line */} - + Settings
diff --git a/src/components/Header/AppHeaderUser.tsx b/src/components/Header/AppHeaderUser.tsx index c7de48a7c4..4fa92eeb84 100644 --- a/src/components/Header/AppHeaderUser.tsx +++ b/src/components/Header/AppHeaderUser.tsx @@ -69,7 +69,7 @@ export function AppHeaderUser({ openSettings, small, disconnectAccountAndCloseSe if (!active || !account) { return (
-
+
{isHomeSite() ? Launch App : Trade} @@ -99,7 +99,7 @@ export function AppHeaderUser({ openSettings, small, disconnectAccountAndCloseSe return (
-
+
{isHomeSite() ? Launch App : Trade} @@ -107,7 +107,7 @@ export function AppHeaderUser({ openSettings, small, disconnectAccountAndCloseSe {showConnectionOptions ? ( <> -
+
)} -
+
{!isMobile && (
diff --git a/src/components/Header/HeaderLink.tsx b/src/components/Header/HeaderLink.tsx index cf5c07b0de..6c8f9ea751 100644 --- a/src/components/Header/HeaderLink.tsx +++ b/src/components/Header/HeaderLink.tsx @@ -16,6 +16,7 @@ type Props = { onClick?: MouseEventHandler; children?: ReactNode; isActive?: NavLinkProps["isActive"]; + qa?: string; }; export function HeaderLink({ @@ -27,6 +28,7 @@ export function HeaderLink({ showRedirectModal, onClick, isActive, + qa, }: Props) { const isOnHomePage = window.location.pathname === "/"; const isHome = isHomeSite(); @@ -73,6 +75,7 @@ export function HeaderLink({ exact={exact} to={to} onClick={onClick} + data-qa={qa} > {children} diff --git a/src/components/LeverageSlider/LeverageSlider.tsx b/src/components/LeverageSlider/LeverageSlider.tsx index 6514b08e53..8982d76d3b 100644 --- a/src/components/LeverageSlider/LeverageSlider.tsx +++ b/src/components/LeverageSlider/LeverageSlider.tsx @@ -62,6 +62,7 @@ export function LeverageSlider(p: Props) { positive: p.isPositive, negative: !p.isPositive, })} + data-qa="leverage-slider" > (function LeverageSl visible={dragging} placement="top" key={index} + data-qa={"leverage-slider-tooltip"} > - + ); }); diff --git a/src/components/MarketSelector/MarketSelector.tsx b/src/components/MarketSelector/MarketSelector.tsx index 3f04fa6ee3..16ed67dd00 100644 --- a/src/components/MarketSelector/MarketSelector.tsx +++ b/src/components/MarketSelector/MarketSelector.tsx @@ -113,6 +113,7 @@ export function MarketSelector({ return (
!state.disabled && onSelectOption(option)} @@ -181,12 +183,12 @@ export function MarketSelector({
{selectedMarketLabel ? ( -
setIsModalVisible(true)}> +
setIsModalVisible(true)} data-qa="market-selector"> {selectedMarketLabel}
) : ( -
setIsModalVisible(true)}> +
setIsModalVisible(true)} data-qa="market-selector"> {marketInfo ? getMarketIndexName(marketInfo) : "..."}
diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx index e699f8169d..cd0856c15e 100644 --- a/src/components/Modal/Modal.tsx +++ b/src/components/Modal/Modal.tsx @@ -31,6 +31,7 @@ export type ModalProps = PropsWithChildren<{ label?: React.ReactNode; headerContent?: () => React.ReactNode; onAfterOpen?: () => void; + qa?: string; }>; export default function Modal({ @@ -42,6 +43,7 @@ export default function Modal({ headerContent, onAfterOpen, setIsVisible, + qa, }: ModalProps) { const modalRef = useRef(null); @@ -83,7 +85,7 @@ export default function Modal({ style={isVisible ? VISIBLE_STYLES : HIDDEN_STYLES} onClick={() => setIsVisible(false)} >
-
+
{label}
diff --git a/src/components/NumberInput/NumberInput.tsx b/src/components/NumberInput/NumberInput.tsx index c0bc066eba..cc008bacfb 100644 --- a/src/components/NumberInput/NumberInput.tsx +++ b/src/components/NumberInput/NumberInput.tsx @@ -14,9 +14,10 @@ type Props = { onBlur?: () => void; className?: string; placeholder?: string; + qa?: string; }; -function NumberInput({ value = "", inputRef, onValueChange, onFocus, onBlur, className, placeholder }: Props) { +function NumberInput({ value = "", inputRef, onValueChange, onFocus, onBlur, className, placeholder, qa }: Props) { function onChange(e: ChangeEvent) { if (!onValueChange) return; // Replace comma with dot @@ -32,6 +33,7 @@ function NumberInput({ value = "", inputRef, onValueChange, onFocus, onBlur, cla } return ( +
{fromTokenAddress && ( )}
-
@@ -915,6 +917,7 @@ export function TradeBox(p: Props) { onInputValueChange={handleToInputTokenChange} showMaxButton={false} preventFocusOnLabelClick="right" + qa="swap-receive" > {toTokenAddress && ( )} @@ -947,6 +951,7 @@ export function TradeBox(p: Props) { inputValue={toTokenInputValue} onInputValueChange={handleToInputTokenChange} showMaxButton={false} + qa="buy" > {toTokenAddress && ( 0 : false} onPercentChange={handleClosePercentageChange} + qa="close" > USD @@ -1005,6 +1011,7 @@ export function TradeBox(p: Props) { onClickTopRightLabel={setMarkPriceAsTriggerPrice} inputValue={triggerPriceInputValue} onInputValueChange={handleTriggerPriceInputChange} + qa="trigger-price" > USD @@ -1020,6 +1027,7 @@ export function TradeBox(p: Props) { onClickTopRightLabel={handleTriggerMarkPriceClick} inputValue={triggerRatioInputValue} onInputValueChange={handleTriggerRatioInputChange} + qa="trigger-price" > {markRatio && ( <> @@ -1323,6 +1331,7 @@ export function TradeBox(p: Props) { const buttonContent = ( @@ -597,6 +598,7 @@ export function PositionEditor(p: Props) { Edit {position?.isLong ? t`Long` : t`Short`} {position?.indexToken?.symbol} } + qa="position-edit-modal" > {position && ( <> @@ -673,6 +675,7 @@ export function PositionEditor(p: Props) { ); } }} + qa="amount-input" > {availableSwapTokens ? ( Date: Tue, 30 Jul 2024 21:26:18 +0300 Subject: [PATCH 03/62] test hooks From c4ac4633c56694178fdc21a84fec2f47378a2630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hub=C3=A9rt=20de=20Lalye?= Date: Thu, 1 Aug 2024 10:29:46 +0400 Subject: [PATCH 04/62] setup autotests subdir --- .eslintignore | 1 + autotests/.gitignore | 10 + autotests/README.md | 29 + autotests/package.json | 21 + autotests/playwright.config.ts | 13 + autotests/src/base.ts | 102 ++ autotests/src/elements/base-page.ts | 69 + autotests/src/elements/common.ts | 30 + autotests/src/elements/elements.ts | 442 +++++ autotests/src/elements/types.ts | 16 + autotests/src/mocks/web3.ts | 15 + autotests/src/tests/nanvigation.spec.ts | 69 + autotests/src/tests/trade.spec.ts | 113 ++ autotests/src/tests/wallet.spec.ts | 11 + autotests/yarn.lock | 1552 +++++++++++++++++ .../NetworkDropdown/NetworkDropdown.tsx | 4 +- src/pages/Buy/Buy.tsx | 4 +- src/pages/BuyGlp/BuyGlp.js | 2 +- src/pages/Dashboard/DashboardV2.tsx | 2 +- src/pages/Ecosystem/Ecosystem.js | 4 +- .../components/LeaderboardContainer.tsx | 4 +- src/pages/MarketPoolsPage/MarketPoolsPage.tsx | 1 + src/pages/Referrals/Referrals.tsx | 2 +- src/pages/Stake/StakeV2.tsx | 1 + 24 files changed, 2506 insertions(+), 11 deletions(-) create mode 100644 .eslintignore create mode 100644 autotests/.gitignore create mode 100644 autotests/README.md create mode 100644 autotests/package.json create mode 100644 autotests/playwright.config.ts create mode 100644 autotests/src/base.ts create mode 100644 autotests/src/elements/base-page.ts create mode 100644 autotests/src/elements/common.ts create mode 100644 autotests/src/elements/elements.ts create mode 100644 autotests/src/elements/types.ts create mode 100644 autotests/src/mocks/web3.ts create mode 100644 autotests/src/tests/nanvigation.spec.ts create mode 100644 autotests/src/tests/trade.spec.ts create mode 100644 autotests/src/tests/wallet.spec.ts create mode 100644 autotests/yarn.lock diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000..95890b052b --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +autotests/ diff --git a/autotests/.gitignore b/autotests/.gitignore new file mode 100644 index 0000000000..cf728ff12e --- /dev/null +++ b/autotests/.gitignore @@ -0,0 +1,10 @@ +node_modules +test-results +.env + +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions diff --git a/autotests/README.md b/autotests/README.md new file mode 100644 index 0000000000..f8375bf940 --- /dev/null +++ b/autotests/README.md @@ -0,0 +1,29 @@ +# Autotests + +This repository contains the autotests for the GMX interface. + +## Prerequisites + +- Git [Installation guide](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +- Node.js >= 20 [Installation guide](https://nodejs.org/en/download/) +- Yarn >= 1.22.5 [Installation guide](https://classic.yarnpkg.com/en/docs/install) + +## Install + +1. Clone the repository to your local machine. +2. Install the dependencies by running `yarn`. + +## How to run autotests locally + +1. Setup `.env` file in same directory as tests with the following content: + +```bash +GMX_BASE_URL= +SEED= +PWDEBUG= +USE_METAMASK= +PW_WORKERS= +NETWORK=arbitrum or fuji +``` + +2. Run tests: `yarn test` diff --git a/autotests/package.json b/autotests/package.json new file mode 100644 index 0000000000..043c2a1b01 --- /dev/null +++ b/autotests/package.json @@ -0,0 +1,21 @@ +{ + "name": "gmx-ui-autotests", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": " npx playwright test", + "postinstall": "npx playwright install" + }, + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "@depay/web3-mock": "^14.18.0", + "@playwright/test": "^1.45.1", + "@tenkeylabs/dappwright": "^2.8.5", + "dotenv": "16.4.5" + }, + "devDependencies": { + "@types/node": "^20.14.11" + } +} diff --git a/autotests/playwright.config.ts b/autotests/playwright.config.ts new file mode 100644 index 0000000000..2b1a9bbfac --- /dev/null +++ b/autotests/playwright.config.ts @@ -0,0 +1,13 @@ +import { PlaywrightTestConfig } from "@playwright/test"; + +const config: PlaywrightTestConfig = { + timeout: process.env.PW_TIMEOUT ? Number(process.env.PW_TIMEOUT) : 60_000, + testDir: "./src/tests", + testMatch: "*.spec.ts", + workers: process.env.PW_WORKERS ? Number(process.env.PW_WORKERS) : 1, + use: { + viewport: { width: 1600, height: 1200 }, + }, +}; + +export default config; diff --git a/autotests/src/base.ts b/autotests/src/base.ts new file mode 100644 index 0000000000..61a1ae869b --- /dev/null +++ b/autotests/src/base.ts @@ -0,0 +1,102 @@ +import { BrowserContext, test as baseTest } from "@playwright/test"; +import dappwright, { Dappwright, MetaMaskWallet } from "@tenkeylabs/dappwright"; +import { GmxPage } from "./elements/elements"; + +import { config } from "dotenv"; +import { mockWeb3 } from "./mocks/web3"; + +config(); + +let savedContext: BrowserContext | undefined; +let savedWallet: Dappwright | undefined; + +export const test = baseTest.extend<{ + context: BrowserContext; + wallet: Dappwright; + appUrl: string; + gmx: GmxPage; +}>({ + page: async ({ context }, use) => { + const page = await context.newPage(); + + if (!process.env.USE_METAMASK) { + await mockWeb3(page, () => { + Web3Mock.mock({ + blockchain: "ethereum", + accounts: { return: ["0xd73b04b0e696b0945283defa3eee453814758f1a"] }, + }); + }); + } + + await use(page); + }, + + context: async ({ appUrl }, use) => { + if (savedContext && savedWallet) { + await use(savedContext); + return; + } + + const [wallet, metamaskPage, ctx] = await dappwright.bootstrap("", { + wallet: "metamask", + version: MetaMaskWallet.recommendedVersion, + seed: process.env.SEED, + headless: !Boolean(process.env.PWDEBUG), + }); + + if (process.env.USE_METAMASK) { + console.log("[Preparing Metamask]"); + if (process.env.CHAIN?.toLocaleLowerCase() === "arbitrum") { + await metamaskPage.locator('[data-testid="network-display"]').click(); + await metamaskPage.locator('button:has-text("Add Network")').click(); + await metamaskPage.locator('//h6[contains(text(), "Arbitrum One")]/../../../*[2]/button').click(); + await metamaskPage.locator('[data-testid="confirmation-submit-button"]').click(); + await metamaskPage.locator('//button/h6[contains(text(), "Switch to Arbitrum One")]').click(); + await metamaskPage.waitForTimeout(1000); + await metamaskPage.locator('[data-testid="detected-token-banner"] button').click(); + await metamaskPage.waitForTimeout(2000); + await metamaskPage + .locator('//*[contains(@class, "popover-footer")]/button[contains(text(), "Import")]') + .click(); + await metamaskPage.waitForTimeout(2000); + } else { + await wallet.addNetwork({ + chainId: 43113, + networkName: "Avalanche Fuji", + rpc: "https://avalanche-fuji-c-chain.publicnode.com", + symbol: "AVAX", + }); + } + } + + console.log("[Preparing GMX App]"); + const page = await ctx.newPage(); + await page.goto(appUrl); + const gmx = new GmxPage(page, wallet, appUrl); + await gmx.header.connectWallet(); + await gmx.closeAllToasts(); + + savedContext = ctx; + savedWallet = wallet; + + await use(ctx); + }, + + wallet: async ({ context }, use) => { + if (savedWallet) { + await use(savedWallet); + return; + } + + const metamask = await dappwright.getWallet("metamask", context); + + await use(metamask); + }, + + appUrl: async ({}, use) => use(process.env.GMX_BASE_URL || "https://app.gmx.io"), + + gmx: async ({ wallet, page, appUrl }, use) => { + const gmx = new GmxPage(page, wallet, appUrl); + await use(gmx); + }, +}); diff --git a/autotests/src/elements/base-page.ts b/autotests/src/elements/base-page.ts new file mode 100644 index 0000000000..e10c7057ab --- /dev/null +++ b/autotests/src/elements/base-page.ts @@ -0,0 +1,69 @@ +import { Locator, Page } from "@playwright/test"; +import { Dappwright } from "@tenkeylabs/dappwright"; + +declare module "@playwright/test" { + interface Locator { + selector: string; + waitForVisible(): void; + waitForSelector(): void; + } +} + +export class BasePage { + public root: Locator; + public wallet: Dappwright; + + private wrapLocatorWithRoot(locator: Locator, selector: string) { + locator.selector = selector; + locator.waitForVisible = async () => { + if (process.env.PWDEBUG) { + console.log(`Waiting for ${selector} to be visible`); + } + + await this.page.waitForSelector(selector, { + state: "visible", + }); + }; + + locator.waitForSelector = async () => { + if (process.env.PWDEBUG) { + console.log(`Waiting for selector = ${selector}`); + } + + await this.page.waitForSelector(selector); + }; + + return locator; + } + + constructor( + public page: Page, + wallet: Dappwright, + root?: Locator + ) { + this.page = page; + this.wallet = wallet; + this.root = root ? root : page.locator("body"); + if (root) { + this.root = root; + } else { + this.root = page.locator("body"); + this.wrapLocatorWithRoot(this.root, "body"); + } + } + + locator(selector: string) { + const locatorSelector = + selector.startsWith(".") || selector.startsWith("//") || selector.startsWith("[") + ? selector + : `[data-qa="${selector}"]`; + const locator = this.root.locator(locatorSelector); + return this.wrapLocatorWithRoot(locator, locatorSelector); + } + + async waitForTransactionToBeSent() { + /** + * @todo Implement me! + */ + } +} diff --git a/autotests/src/elements/common.ts b/autotests/src/elements/common.ts new file mode 100644 index 0000000000..2385c3ada5 --- /dev/null +++ b/autotests/src/elements/common.ts @@ -0,0 +1,30 @@ +import { Locator, Page } from "@playwright/test"; +import { Dappwright } from "@tenkeylabs/dappwright"; +import { BasePage } from "./base-page"; + +export class Tabs extends BasePage { + tabs: Record; + + setTabs(tabs: Record) { + this.tabs = tabs; + } + + async select(value: T) { + const tab = this.tabs[value]; + + const style = await tab.evaluate((el) => { + return window.getComputedStyle(el).pointerEvents; + }); + + if (style !== "none") { + await tab.click(); + } + } +} + +export class Modal extends BasePage { + constructor(page: Page, wallet: Dappwright, modalAttribute: string) { + super(page, wallet); + this.root = this.locator(modalAttribute); + } +} diff --git a/autotests/src/elements/elements.ts b/autotests/src/elements/elements.ts new file mode 100644 index 0000000000..6a940f72f9 --- /dev/null +++ b/autotests/src/elements/elements.ts @@ -0,0 +1,442 @@ +import { Locator, Page, expect } from "@playwright/test"; +import { Dappwright } from "@tenkeylabs/dappwright"; +import { BasePage } from "./base-page"; + +import { Direction, TradeMode, GmxNavigation, Leverage, EditOperation, CloseOperation } from "./types"; +import { Modal, Tabs } from "./common"; + +class TradeDirectionTabs extends Tabs { + long = this.locator("trade-direction-tab-Long"); + short = this.locator("trade-direction-tab-Short"); + swap = this.locator("trade-direction-tab-Swap"); + + constructor(page: Page, wallet: Dappwright, root: Locator) { + super(page, wallet, root); + this.setTabs({ + Long: this.long, + Short: this.short, + Swap: this.swap, + }); + } +} + +class TradeModeTabs extends Tabs { + market = this.locator("trade-mode-tab-Market"); + limit = this.locator("trade-mode-tab-Limit"); + trigger = this.locator("trade-mode-tab-Trigger"); + + constructor(page: Page, wallet: Dappwright, root: Locator) { + super(page, wallet, root); + this.setTabs({ + Market: this.market, + Limit: this.limit, + Trigger: this.trigger, + }); + } +} + +class PositionEditTabs extends Tabs { + depositTab = this.locator("operation-tabs-tab-Deposit"); + withdrawTab = this.locator("operation-tabs-tab-Withdraw"); + + constructor(page: Page, wallet: Dappwright, root: Locator) { + super(page, wallet, root); + this.setTabs({ + Deposit: this.depositTab, + Withdraw: this.withdrawTab, + }); + } +} + +class PositionCloseTabs extends Tabs { + marketTab = this.locator("operation-tabs-tab-Market"); + tpsl = this.locator("operation-tabs-tab-TP/SL"); + + constructor(page: Page, wallet: Dappwright, root: Locator) { + super(page, wallet, root); + this.setTabs({ + Market: this.marketTab, + "TP/SL": this.tpsl, + }); + } +} + +class LeverageSlider extends BasePage { + tooltip = this.locator("leverage-slider-tooltip"); + handle = this.locator("leverage-slider-handle"); + + async setLeverage(leverage: Leverage) { + const handle = await this.handle; + const leverageTextTarget = await this.page.locator( + `xpath=//*[contains(@class, "rc-slider-mark-text") and text()="${leverage}"]` + ); + await handle.dragTo(leverageTextTarget); + } +} + +class Tradebox extends BasePage { + market = this.locator("market"); + payInput = this.locator("pay-input"); + buyInput = this.locator("buy-input"); + triggerPriceInput = this.locator("trigger-price-input"); + + confirmTradeButton = this.locator("confirm-trade-button"); + + marketSelector = this.locator("market-selector"); + collateralSelector = this.locator("collateral-selector"); + + poolSelector = this.locator("pool-selector-button"); + collateralInSelector = this.locator("collateral-in-selector-button"); + + leverageSlider = new LeverageSlider(this.page, this.wallet, this.locator("leverage-slider")); + + directionTabs = new TradeDirectionTabs(this.page, this.wallet, this.locator("trade-direction")); + + modeTabs = new TradeModeTabs(this.page, this.wallet, this.locator("trade-mode")); + + async selectDirection(direction: Direction) { + await this.directionTabs.select(direction); + } + + async selectMode(mode: TradeMode) { + await this.modeTabs.select(mode); + } + + async setLeverage(leverage: Leverage) { + await this.leverageSlider.setLeverage(leverage); + } + + async selectMarket(market: string) { + await this.page.waitForSelector(this.marketSelector.selector); + await this.marketSelector.click(); + + const marketsModal = new Modal(this.page, this.wallet, "market-selector-modal"); + + await marketsModal.root.waitForVisible(); + + const item = await marketsModal.locator(`market-selector-${market}`); + + await item.focus(); + await item.waitForVisible(); + await item.click(); + + await this.page.waitForSelector(marketsModal.root.selector, { + state: "detached", + }); + } + + async selectCollateral(token: string) { + await this.page.waitForSelector(this.collateralSelector.selector); + await this.collateralSelector.click(); + + const tokensModal = new Modal(this.page, this.wallet, "collateral-selector-modal"); + + await tokensModal.root.waitForVisible(); + + const item = tokensModal.locator(`collateral-selector-token-${token}`); + + await item.focus(); + await item.waitForVisible(); + await item.click(); + + await this.page.waitForSelector(tokensModal.root.selector, { + state: "detached", + }); + } + + async selectPool(pool: string) { + await this.poolSelector.click(); + + const item = this.page.locator(`[data-qa="pool-selector-row-${pool}"]`); + await item; + await item.click(); + + await this.page.waitForSelector(`[data-qa="pool-selector-row-${pool}"]`, { + state: "detached", + }); + } + + async selectCollateralIn(token: string) { + await this.collateralInSelector.click(); + + const item = this.page.locator(`[data-qa="collateral-in-selector-row-${token}"]`); + await item; + await item.click(); + + await this.page.waitForSelector(`[data-qa="collateral-in-selector-row-${token}"]`, { + state: "detached", + }); + } +} + +class Header extends BasePage { + settings = this.locator("settings"); + userAddress = this.locator("user-address"); + + leaderboard = this.locator("leaderboard"); + ecosystem = this.locator("ecosystem"); + + connectWalletButton = this.locator("connect-wallet-button"); + + price = this.locator("price"); + + async connectWallet() { + const connect = await this.connectWalletButton; + + if (!connect) { + console.error("Wallet is already connected"); + return; + } + + await connect.click(); + await this.page.waitForSelector('[data-testid="rk-wallet-option-io.metamask"]'); + await this.page.click('[data-testid="rk-wallet-option-io.metamask"]'); + await this.wallet.approve(); + } + + async getPrice(): Promise { + const result = await this.price.evaluate((el) => el.textContent); + + if (!result) { + return ""; + } + + return result; + } +} + +class NetworkDropdown extends BasePage { + handle = this.locator("networks-dropdown-handle"); + dropdown = this.locator("networks-dropdown"); + settings = this.locator("networks-dropdown-settings"); + + async switchNetwork(network: "Arbitrum" | "Avalanche" | "Arbitrum Goerli" | "Avalanche Fuji") { + await this.handle.click(); + await this.dropdown.waitForVisible(); + const item = await this.locator(`networks-dropdown-${network}`); + await item.click(); + await this.wallet.confirmNetworkSwitch(); + } +} + +class Settings extends BasePage { + modal = new Modal(this.page, this.wallet, "settings-modal"); +} + +class EditModal extends Modal { + input = this.locator("amount-input"); + confirmButton = this.locator("confirm-button"); + tabs = new PositionEditTabs(this.page, this.wallet, this.root); + + async deposit(amount: string) { + await this.tabs.select("Deposit"); + await this.input.locator("input").fill(amount); + + expect(this.confirmButton).toBeEnabled(); + + await this.confirmButton.click(); + await this.wallet.confirmTransaction(); + } + + async withdraw(amount: string) { + await this.tabs.select("Withdraw"); + + if (amount.endsWith("%")) { + await this.input.locator("input").click(); + await this.input.locator(`[data-qa="amount-input-percent-selector-${amount.slice(0, -1)}"]`).click(); + } else { + await this.input.fill(amount); + } + + expect(this.confirmButton).toBeEnabled(); + + await this.confirmButton.click(); + await this.wallet.confirmTransaction(); + } + + async confirm() { + await this.confirmButton.click(); + } +} + +class CloseModal extends Modal { + input = this.locator("amount-input"); + confirmButton = this.locator("confirm-button"); + tabs = new PositionCloseTabs(this.page, this.wallet, this.root); + + async closePartially(amount: string) { + await this.tabs.select("Market"); + + if (amount.endsWith("%")) { + await this.input.locator("input").click(); + await this.input.locator(`[data-qa="amount-input-percent-selector-${amount.slice(0, -1)}"]`).click(); + } else { + await this.input.fill(amount); + } + + expect(this.confirmButton).toBeEnabled(); + + await this.confirmButton.click(); + await this.wallet.confirmTransaction(); + } + + async closeFull() { + await this.tabs.select("Market"); + + await this.input.locator("input").click(); + await this.input.locator(`[data-qa="amount-input-max"]`).click(); + + expect(this.confirmButton).toBeEnabled(); + + await this.confirmButton.click(); + await this.wallet.confirmTransaction(); + } + + async confirm() { + await this.confirmButton.click(); + } +} + +class Position extends BasePage { + closeButton = this.locator("position-close-button"); + edit = this.locator("position-edit-button"); + + editModal?: EditModal; + closeModal?: CloseModal; + + async getCollateral(): Promise { + return await this.locator("position-collateral-value").evaluate((el) => el.textContent); + } + + async startEdit() { + await this.edit.click(); + this.editModal = new EditModal(this.page, this.wallet, "position-edit-modal"); + await this.editModal.root.waitForVisible(); + } + + async startClose() { + await this.closeButton.click(); + this.closeModal = new CloseModal(this.page, this.wallet, "position-close-modal"); + await this.closeModal.root.waitForVisible(); + } + + async closeFull() { + await this.startClose(); + + if (!this.closeModal) { + throw new Error("Close modal is not opened"); + } + + await this.closeModal.closeFull(); + await this.closeModal.root.waitFor({ + state: "detached", + }); + this.closeModal = undefined; + } + + async closePartially(amount: string) { + await this.startClose(); + + if (!this.closeModal) { + throw new Error("Close modal is not opened"); + } + + await this.closeModal.closePartially(amount); + await this.closeModal.root.waitFor({ + state: "detached", + }); + this.closeModal = undefined; + } + + async deposit(amount: string) { + await this.startEdit(); + + if (!this.editModal) { + throw new Error("Edit modal is not opened"); + } + + await this.editModal.deposit(amount); + await this.editModal.root.waitFor({ + state: "detached", + }); + this.editModal = undefined; + + this.waitForTransactionToBeSent(); + } + + async withdraw(amount: string) { + await this.startEdit(); + + if (!this.editModal) { + throw new Error("Edit modal is not opened"); + } + + await this.editModal.withdraw(amount); + await this.editModal.root.waitFor({ + state: "detached", + }); + this.editModal = undefined; + } +} + +export class GmxPage extends BasePage { + header = new Header(this.page, this.wallet, this.locator("header")); + tradebox = new Tradebox(this.page, this.wallet, this.locator("tradebox")); + + tradeLink = this.locator("trade"); + + dashboardPage = this.locator("dashboard-page"); + earnPage = this.locator("earn-page"); + leaderboardPage = this.locator("leaderboard-page"); + ecosystemPage = this.locator("ecosystem-page"); + referralsPage = this.locator("referrals-page"); + buyPage = this.locator("buy-page"); + buyGlpPage = this.locator("buy-glp-page"); + poolsPage = this.locator("pools-page"); + + networksDropdown = new NetworkDropdown(this.page, this.wallet); + settings = new Settings(this.page, this.wallet); + baseUrl: string; + + constructor(page: Page, wallet: Dappwright, baseUrl: string) { + super(page, wallet); + this.baseUrl = baseUrl; + } + + async closeAllToasts() { + while (true) { + const element = await this.page.$("[data-qa='close-toast']"); + if (!element) { + break; + } + + await element.click(); + await element.waitForElementState("hidden"); + /** We need to wait for a second to ensure that DOM node will be detached */ + await this.page.waitForTimeout(500); + } + } + + async getPosition(market: string, pool: string, direction: "Long" | "Short") { + const position = new Position( + this.page, + this.wallet, + this.locator(`[data-qa="trade-table-large"] [data-qa="position-item-${market}-${pool}-${direction}"]`) + ); + + await position.root.waitForVisible(); + + return position; + } + + async navigateTo(page: GmxNavigation) { + await this.page.goto(this.baseUrl + "#" + page); + } + + async openSettings() { + await this.networksDropdown.handle.waitForSelector(); + await this.networksDropdown.handle.click(); + await this.networksDropdown.dropdown.waitForVisible(); + await this.networksDropdown.settings.click(); + } +} diff --git a/autotests/src/elements/types.ts b/autotests/src/elements/types.ts new file mode 100644 index 0000000000..10905e1488 --- /dev/null +++ b/autotests/src/elements/types.ts @@ -0,0 +1,16 @@ +export type Direction = "Long" | "Short" | "Swap"; +export type TradeMode = "Market" | "Limit" | "Trigger"; +export type Leverage = `${0.1 | 1 | 2 | 5 | 10 | 15 | 25 | 50 | 75 | 100}x`; +export type PositionPercent = "25%" | "50%" | "75%" | "100%"; +export type GmxNavigation = + | "/trade" + | "/dashboard" + | "/earn" + | "/leaderboard" + | "/ecosystem" + | "/buy" + | "/buy_glp" + | "/pools" + | "/referrals"; +export type EditOperation = "Deposit" | "Withdraw"; +export type CloseOperation = "Market" | "TP/SL"; diff --git a/autotests/src/mocks/web3.ts b/autotests/src/mocks/web3.ts new file mode 100644 index 0000000000..14f0b6cb03 --- /dev/null +++ b/autotests/src/mocks/web3.ts @@ -0,0 +1,15 @@ +import { Page } from "@playwright/test"; +import { readFileSync } from "fs"; + +declare global { + export const Web3Mock: any; +} + +export const mockWeb3 = async (page: Page, fn: Function) => { + await page.addInitScript({ + content: + readFileSync(require.resolve("@depay/web3-mock/dist/umd/index.bundle.js"), "utf-8") + + "\n" + + `(${fn.toString()})();`, + }); +}; diff --git a/autotests/src/tests/nanvigation.spec.ts b/autotests/src/tests/nanvigation.spec.ts new file mode 100644 index 0000000000..aca5ecc0bd --- /dev/null +++ b/autotests/src/tests/nanvigation.spec.ts @@ -0,0 +1,69 @@ +import { expect } from "@playwright/test"; +import { test } from "../base"; + +test.describe("Should navigate across all pages with no crashes", () => { + test.afterEach(async ({ gmx }) => { + await gmx.page.close(); + }); + + test("/trade", async ({ gmx }) => { + await gmx.navigateTo("/trade"); + await gmx.tradebox.root.waitForSelector(); + expect(gmx.tradebox.root).toBeAttached(); + }); + + test("/dashboard", async ({ gmx }) => { + await gmx.navigateTo("/dashboard"); + await gmx.dashboardPage.waitForSelector(); + expect(gmx.dashboardPage).toBeAttached(); + }); + + test("/earn", async ({ gmx }) => { + await gmx.navigateTo("/earn"); + await gmx.earnPage.waitForSelector(); + expect(gmx.earnPage).toBeAttached(); + }); + + test("/leaderboard", async ({ gmx }) => { + await gmx.navigateTo("/leaderboard"); + await gmx.leaderboardPage.waitForSelector(); + expect(gmx.leaderboardPage).toBeAttached(); + }); + + test("/ecosystem", async ({ gmx }) => { + await gmx.navigateTo("/ecosystem"); + await gmx.ecosystemPage.waitForSelector(); + expect(gmx.ecosystemPage).toBeAttached(); + }); + + test("/buy_glp", async ({ gmx }) => { + await gmx.navigateTo("/buy_glp"); + await gmx.buyGlpPage.waitForSelector(); + expect(gmx.buyGlpPage).toBeAttached(); + }); + + test("/buy", async ({ gmx }) => { + await gmx.navigateTo("/buy"); + await gmx.buyPage.waitForSelector(); + expect(gmx.buyPage).toBeAttached(); + }); + + test("/pools", async ({ gmx }) => { + await gmx.navigateTo("/pools"); + await gmx.poolsPage.waitForSelector(); + expect(gmx.poolsPage).toBeAttached(); + }); + + test("/referrals", async ({ gmx }) => { + await gmx.navigateTo("/referrals"); + await gmx.referralsPage.waitForSelector(); + expect(gmx.referralsPage).toBeAttached(); + }); + + test("open settings modal", async ({ gmx }) => { + await gmx.navigateTo("/trade"); + await gmx.openSettings(); + await gmx.settings.modal.root.waitForSelector(); + expect(await gmx.settings.modal.root).toBeAttached(); + }); +}); diff --git a/autotests/src/tests/trade.spec.ts b/autotests/src/tests/trade.spec.ts new file mode 100644 index 0000000000..d85def33e2 --- /dev/null +++ b/autotests/src/tests/trade.spec.ts @@ -0,0 +1,113 @@ +import { expect } from "@playwright/test"; +import { test } from "../base"; +import get from "lodash/get"; + +test.describe.serial("Trades", () => { + test.describe("Market", () => { + test("increase market position", async ({ page, gmx }) => { + await page.goto(gmx.baseUrl); + + await gmx.tradebox.selectDirection("Long"); + await gmx.tradebox.selectMode("Market"); + + await gmx.tradebox.selectMarket("BNB/USD"); + await gmx.tradebox.selectCollateral("AVAX"); + + await gmx.tradebox.selectPool("WBTC-USDC"); + await gmx.tradebox.selectCollateralIn("USDC"); + + await gmx.tradebox.payInput.fill("0.1"); + await gmx.tradebox.setLeverage("2x"); + + expect(gmx.tradebox.confirmTradeButton).toBeEnabled(); + + await gmx.tradebox.confirmTradeButton.click(); + await gmx.wallet.confirmTransaction(); + + const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); + + await position.root.waitForSelector(); + expect(position.root).toBeVisible(); + }); + + test("edit position deposit", async ({ page, gmx }) => { + await page.goto(gmx.baseUrl); + + const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); + + expect(position.root).toBeVisible(); + const collateral = await position.getCollateral(); + + await position.deposit("1"); + + const newCollateral = await position.getCollateral(); + expect(newCollateral !== collateral).toBeTruthy(); + }); + + test("edit position withdraw 25%", async ({ page, gmx }) => { + await page.goto(gmx.baseUrl); + + const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); + + expect(position.root).toBeVisible(); + const collateral = await position.getCollateral(); + + await position.withdraw("25%"); + + const newCollateral = await position.getCollateral(); + expect(newCollateral !== collateral).toBeTruthy(); + }); + + test("close position partially", async ({ page, gmx }) => { + await page.goto(gmx.baseUrl); + + const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); + + expect(position.root).toBeVisible(); + await position.closePartially("25%"); + }); + + test("close position full", async ({ page, gmx }) => { + await page.goto(gmx.baseUrl); + + const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); + + expect(position.root).toBeVisible(); + await position.closeFull(); + }); + }); + + test.describe("Limit", () => { + test("create limit position", async ({ page, gmx }) => { + await page.goto(gmx.baseUrl); + + await gmx.tradebox.selectDirection("Long"); + await gmx.tradebox.selectMode("Limit"); + + await gmx.tradebox.selectMarket("BNB/USD"); + await gmx.tradebox.selectCollateral("AVAX"); + + await gmx.tradebox.selectPool("WBTC-USDC"); + await gmx.tradebox.selectCollateralIn("USDC"); + + await gmx.tradebox.payInput.fill("0.1"); + await gmx.tradebox.setLeverage("2x"); + + const price = await gmx.header.getPrice(); + + const limitPrice = Number(price.replace(/[\$,]/g, "")) * 0.9; + + await gmx.tradebox.triggerPriceInput.fill(limitPrice.toString()); + + expect(gmx.tradebox.confirmTradeButton).toBeEnabled(); + + await gmx.tradebox.confirmTradeButton.click(); + await gmx.wallet.confirmTransaction(); + + const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); + + await position.root.waitForSelector(); + expect(position.root).toBeVisible(); + }); + }); +}); diff --git a/autotests/src/tests/wallet.spec.ts b/autotests/src/tests/wallet.spec.ts new file mode 100644 index 0000000000..ae34ca56b8 --- /dev/null +++ b/autotests/src/tests/wallet.spec.ts @@ -0,0 +1,11 @@ +import { expect } from "@playwright/test"; +import { test } from "../base"; + +test.describe("Wallet", () => { + test("Should able to connect wallet", async ({ page, gmx }) => { + await page.goto(gmx.baseUrl); + await page.waitForSelector(gmx.header.userAddress.selector); + const element = await page.$(gmx.header.userAddress.selector); + expect(element).not.toBeNull(); + }); +}); diff --git a/autotests/yarn.lock b/autotests/yarn.lock new file mode 100644 index 0000000000..f740a90f24 --- /dev/null +++ b/autotests/yarn.lock @@ -0,0 +1,1552 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 5 + cacheKey: 8 + +"@depay/solana-web3.js@npm:^1.26.0": + version: 1.26.0 + resolution: "@depay/solana-web3.js@npm:1.26.0" + dependencies: + bs58: ^5.0.0 + checksum: fc54905b30c195c51e498a037b59fcdbc7e41b14728c1437cbb0d834749d6887d86516e10c000ac0dae277def50d11746f499a53c6d4ee8307c0ad73dfd1e278 + languageName: node + linkType: hard + +"@depay/web3-blockchains@npm:^9.1.4": + version: 9.4.2 + resolution: "@depay/web3-blockchains@npm:9.4.2" + checksum: 1372d2ad4dfd150c02a2609ba2c46b439e6f3d4bae8780ea829f9efa755965dfdc8057e59b9a2ba6042af961509e19eb0e535caa8088c84868f1d28e207837f5 + languageName: node + linkType: hard + +"@depay/web3-mock@npm:^14.18.0": + version: 14.18.0 + resolution: "@depay/web3-mock@npm:14.18.0" + dependencies: + "@depay/solana-web3.js": ^1.26.0 + "@depay/web3-blockchains": ^9.1.4 + ethers: ^5.7.1 + checksum: f29dd6f9e40e4805c686a340a4fe63e1f88ba1694bb9ce0251a248e5307887252d194eb4dcad5ac29001094f9d24dbaace420d35adaf1bdc9d584752da188c61 + languageName: node + linkType: hard + +"@ethersproject/abi@npm:5.7.0, @ethersproject/abi@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/abi@npm:5.7.0" + dependencies: + "@ethersproject/address": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/hash": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + checksum: bc6962bb6cb854e4d2a4d65b2c49c716477675b131b1363312234bdbb7e19badb7d9ce66f4ca2a70ae2ea84f7123dbc4e300a1bfe5d58864a7eafabc1466627e + languageName: node + linkType: hard + +"@ethersproject/abstract-provider@npm:5.7.0, @ethersproject/abstract-provider@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/abstract-provider@npm:5.7.0" + dependencies: + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/networks": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 + "@ethersproject/web": ^5.7.0 + checksum: 74cf4696245cf03bb7cc5b6cbf7b4b89dd9a79a1c4688126d214153a938126d4972d42c93182198653ce1de35f2a2cad68be40337d4774b3698a39b28f0228a8 + languageName: node + linkType: hard + +"@ethersproject/abstract-signer@npm:5.7.0, @ethersproject/abstract-signer@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/abstract-signer@npm:5.7.0" + dependencies: + "@ethersproject/abstract-provider": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + checksum: a823dac9cfb761e009851050ebebd5b229d1b1cc4a75b125c2da130ff37e8218208f7f9d1386f77407705b889b23d4a230ad67185f8872f083143e0073cbfbe3 + languageName: node + linkType: hard + +"@ethersproject/address@npm:5.7.0, @ethersproject/address@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/address@npm:5.7.0" + dependencies: + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/rlp": ^5.7.0 + checksum: 64ea5ebea9cc0e845c413e6cb1e54e157dd9fc0dffb98e239d3a3efc8177f2ff798cd4e3206cf3660ee8faeb7bef1a47dc0ebef0d7b132c32e61e550c7d4c843 + languageName: node + linkType: hard + +"@ethersproject/base64@npm:5.7.0, @ethersproject/base64@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/base64@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + checksum: 7dd5d734d623582f08f665434f53685041a3d3b334a0e96c0c8afa8bbcaab934d50e5b6b980e826a8fde8d353e0b18f11e61faf17468177274b8e7c69cd9742b + languageName: node + linkType: hard + +"@ethersproject/basex@npm:5.7.0, @ethersproject/basex@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/basex@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + checksum: 326087b7e1f3787b5fe6cd1cf2b4b5abfafbc355a45e88e22e5e9d6c845b613ffc5301d629b28d5c4d5e2bfe9ec424e6782c804956dff79be05f0098cb5817de + languageName: node + linkType: hard + +"@ethersproject/bignumber@npm:5.7.0, @ethersproject/bignumber@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/bignumber@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + bn.js: ^5.2.1 + checksum: 8c9a134b76f3feb4ec26a5a27379efb4e156b8fb2de0678a67788a91c7f4e30abe9d948638458e4b20f2e42380da0adacc7c9389d05fce070692edc6ae9b4904 + languageName: node + linkType: hard + +"@ethersproject/bytes@npm:5.7.0, @ethersproject/bytes@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/bytes@npm:5.7.0" + dependencies: + "@ethersproject/logger": ^5.7.0 + checksum: 66ad365ceaab5da1b23b72225c71dce472cf37737af5118181fa8ab7447d696bea15ca22e3a0e8836fdd8cfac161afe321a7c67d0dde96f9f645ddd759676621 + languageName: node + linkType: hard + +"@ethersproject/constants@npm:5.7.0, @ethersproject/constants@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/constants@npm:5.7.0" + dependencies: + "@ethersproject/bignumber": ^5.7.0 + checksum: 6d4b1355747cce837b3e76ec3bde70e4732736f23b04f196f706ebfa5d4d9c2be50904a390d4d40ce77803b98d03d16a9b6898418e04ba63491933ce08c4ba8a + languageName: node + linkType: hard + +"@ethersproject/contracts@npm:5.7.0": + version: 5.7.0 + resolution: "@ethersproject/contracts@npm:5.7.0" + dependencies: + "@ethersproject/abi": ^5.7.0 + "@ethersproject/abstract-provider": ^5.7.0 + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/address": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 + checksum: 6ccf1121cba01b31e02f8c507cb971ab6bfed85706484a9ec09878ef1594a62215f43c4fdef8f4a4875b99c4a800bc95e3be69b1803f8ce479e07634b5a740c0 + languageName: node + linkType: hard + +"@ethersproject/hash@npm:5.7.0, @ethersproject/hash@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/hash@npm:5.7.0" + dependencies: + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/address": ^5.7.0 + "@ethersproject/base64": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + checksum: 6e9fa8d14eb08171cd32f17f98cc108ec2aeca74a427655f0d689c550fee0b22a83b3b400fad7fb3f41cf14d4111f87f170aa7905bcbcd1173a55f21b06262ef + languageName: node + linkType: hard + +"@ethersproject/hdnode@npm:5.7.0, @ethersproject/hdnode@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/hdnode@npm:5.7.0" + dependencies: + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/basex": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/pbkdf2": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/sha2": ^5.7.0 + "@ethersproject/signing-key": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 + "@ethersproject/wordlists": ^5.7.0 + checksum: bfe5ca2d89a42de73655f853170ef4766b933c5f481cddad709b3aca18823275b096e572f92d1602a052f80b426edde44ad6b9d028799775a7dad4a5bbed2133 + languageName: node + linkType: hard + +"@ethersproject/json-wallets@npm:5.7.0, @ethersproject/json-wallets@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/json-wallets@npm:5.7.0" + dependencies: + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/address": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/hdnode": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/pbkdf2": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/random": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 + aes-js: 3.0.0 + scrypt-js: 3.0.1 + checksum: f583458d22db62efaaf94d38dd243482776a45bf90f9f3882fbad5aa0b8fd288b41eb7c1ff8ec0b99c9b751088e43d6173530db64dd33c59f9d8daa8d7ad5aa2 + languageName: node + linkType: hard + +"@ethersproject/keccak256@npm:5.7.0, @ethersproject/keccak256@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/keccak256@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + js-sha3: 0.8.0 + checksum: ff70950d82203aab29ccda2553422cbac2e7a0c15c986bd20a69b13606ed8bb6e4fdd7b67b8d3b27d4f841e8222cbaccd33ed34be29f866fec7308f96ed244c6 + languageName: node + linkType: hard + +"@ethersproject/logger@npm:5.7.0, @ethersproject/logger@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/logger@npm:5.7.0" + checksum: 075ab2f605f1fd0813f2e39c3308f77b44a67732b36e712d9bc085f22a84aac4da4f71b39bee50fe78da3e1c812673fadc41180c9970fe5e486e91ea17befe0d + languageName: node + linkType: hard + +"@ethersproject/networks@npm:5.7.1, @ethersproject/networks@npm:^5.7.0": + version: 5.7.1 + resolution: "@ethersproject/networks@npm:5.7.1" + dependencies: + "@ethersproject/logger": ^5.7.0 + checksum: 0339f312304c17d9a0adce550edb825d4d2c8c9468c1634c44172c67a9ed256f594da62c4cda5c3837a0f28b7fabc03aca9b492f68ff1fdad337ee861b27bd5d + languageName: node + linkType: hard + +"@ethersproject/pbkdf2@npm:5.7.0, @ethersproject/pbkdf2@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/pbkdf2@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/sha2": ^5.7.0 + checksum: b895adb9e35a8a127e794f7aadc31a2424ef355a70e51cde10d457e3e888bb8102373199a540cf61f2d6b9a32e47358f9c65b47d559f42bf8e596b5fd67901e9 + languageName: node + linkType: hard + +"@ethersproject/properties@npm:5.7.0, @ethersproject/properties@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/properties@npm:5.7.0" + dependencies: + "@ethersproject/logger": ^5.7.0 + checksum: 6ab0ccf0c3aadc9221e0cdc5306ce6cd0df7f89f77d77bccdd1277182c9ead0202cd7521329ba3acde130820bf8af299e17cf567d0d497c736ee918207bbf59f + languageName: node + linkType: hard + +"@ethersproject/providers@npm:5.7.2": + version: 5.7.2 + resolution: "@ethersproject/providers@npm:5.7.2" + dependencies: + "@ethersproject/abstract-provider": ^5.7.0 + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/address": ^5.7.0 + "@ethersproject/base64": ^5.7.0 + "@ethersproject/basex": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/hash": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/networks": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/random": ^5.7.0 + "@ethersproject/rlp": ^5.7.0 + "@ethersproject/sha2": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 + "@ethersproject/web": ^5.7.0 + bech32: 1.1.4 + ws: 7.4.6 + checksum: 1754c731a5ca6782ae9677f4a9cd8b6246c4ef21a966c9a01b133750f3c578431ec43ec254e699969c4a0f87e84463ded50f96b415600aabd37d2056aee58c19 + languageName: node + linkType: hard + +"@ethersproject/random@npm:5.7.0, @ethersproject/random@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/random@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + checksum: 017829c91cff6c76470852855108115b0b52c611b6be817ed1948d56ba42d6677803ec2012aa5ae298a7660024156a64c11fcf544e235e239ab3f89f0fff7345 + languageName: node + linkType: hard + +"@ethersproject/rlp@npm:5.7.0, @ethersproject/rlp@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/rlp@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + checksum: bce165b0f7e68e4d091c9d3cf47b247cac33252df77a095ca4281d32d5eeaaa3695d9bc06b2b057c5015353a68df89f13a4a54a72e888e4beeabbe56b15dda6e + languageName: node + linkType: hard + +"@ethersproject/sha2@npm:5.7.0, @ethersproject/sha2@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/sha2@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + hash.js: 1.1.7 + checksum: 09321057c022effbff4cc2d9b9558228690b5dd916329d75c4b1ffe32ba3d24b480a367a7cc92d0f0c0b1c896814d03351ae4630e2f1f7160be2bcfbde435dbc + languageName: node + linkType: hard + +"@ethersproject/signing-key@npm:5.7.0, @ethersproject/signing-key@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/signing-key@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + bn.js: ^5.2.1 + elliptic: 6.5.4 + hash.js: 1.1.7 + checksum: 8f8de09b0aac709683bbb49339bc0a4cd2f95598f3546436c65d6f3c3a847ffa98e06d35e9ed2b17d8030bd2f02db9b7bd2e11c5cf8a71aad4537487ab4cf03a + languageName: node + linkType: hard + +"@ethersproject/solidity@npm:5.7.0": + version: 5.7.0 + resolution: "@ethersproject/solidity@npm:5.7.0" + dependencies: + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/sha2": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + checksum: 9a02f37f801c96068c3e7721f83719d060175bc4e80439fe060e92bd7acfcb6ac1330c7e71c49f4c2535ca1308f2acdcb01e00133129aac00581724c2d6293f3 + languageName: node + linkType: hard + +"@ethersproject/strings@npm:5.7.0, @ethersproject/strings@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/strings@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + checksum: 5ff78693ae3fdf3cf23e1f6dc047a61e44c8197d2408c42719fef8cb7b7b3613a4eec88ac0ed1f9f5558c74fe0de7ae3195a29ca91a239c74b9f444d8e8b50df + languageName: node + linkType: hard + +"@ethersproject/transactions@npm:5.7.0, @ethersproject/transactions@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/transactions@npm:5.7.0" + dependencies: + "@ethersproject/address": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/rlp": ^5.7.0 + "@ethersproject/signing-key": ^5.7.0 + checksum: a31b71996d2b283f68486241bff0d3ea3f1ba0e8f1322a8fffc239ccc4f4a7eb2ea9994b8fd2f093283fd75f87bae68171e01b6265261f821369aca319884a79 + languageName: node + linkType: hard + +"@ethersproject/units@npm:5.7.0": + version: 5.7.0 + resolution: "@ethersproject/units@npm:5.7.0" + dependencies: + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + checksum: 304714f848cd32e57df31bf545f7ad35c2a72adae957198b28cbc62166daa929322a07bff6e9c9ac4577ab6aa0de0546b065ed1b2d20b19e25748b7d475cb0fc + languageName: node + linkType: hard + +"@ethersproject/wallet@npm:5.7.0": + version: 5.7.0 + resolution: "@ethersproject/wallet@npm:5.7.0" + dependencies: + "@ethersproject/abstract-provider": ^5.7.0 + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/address": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/hash": ^5.7.0 + "@ethersproject/hdnode": ^5.7.0 + "@ethersproject/json-wallets": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/random": ^5.7.0 + "@ethersproject/signing-key": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 + "@ethersproject/wordlists": ^5.7.0 + checksum: a4009bf7331eddab38e3015b5e9101ef92de7f705b00a6196b997db0e5635b6d83561674d46c90c6f77b87c0500fe4a6b0183ba13749efc22db59c99deb82fbd + languageName: node + linkType: hard + +"@ethersproject/web@npm:5.7.1, @ethersproject/web@npm:^5.7.0": + version: 5.7.1 + resolution: "@ethersproject/web@npm:5.7.1" + dependencies: + "@ethersproject/base64": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + checksum: 7028c47103f82fd2e2c197ce0eecfacaa9180ffeec7de7845b1f4f9b19d84081b7a48227aaddde05a4aaa526af574a9a0ce01cc0fc75e3e371f84b38b5b16b2b + languageName: node + linkType: hard + +"@ethersproject/wordlists@npm:5.7.0, @ethersproject/wordlists@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/wordlists@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/hash": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + checksum: 30eb6eb0731f9ef5faa44bf9c0c6e950bcaaef61e4d2d9ce0ae6d341f4e2d6d1f4ab4f8880bfce03b7aac4b862fb740e1421170cfbf8e2aafc359277d49e6e97 + languageName: node + linkType: hard + +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: ^5.1.2 + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: ^7.0.1 + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: ^8.1.0 + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 4a473b9b32a7d4d3cfb7a614226e555091ff0c5a29a1734c28c72a182c2f6699b26fc6b5c2131dfd841e86b185aea714c72201d7c98c2fba5f17709333a67aeb + languageName: node + linkType: hard + +"@npmcli/agent@npm:^2.0.0": + version: 2.2.2 + resolution: "@npmcli/agent@npm:2.2.2" + dependencies: + agent-base: ^7.1.0 + http-proxy-agent: ^7.0.0 + https-proxy-agent: ^7.0.1 + lru-cache: ^10.0.1 + socks-proxy-agent: ^8.0.3 + checksum: 67de7b88cc627a79743c88bab35e023e23daf13831a8aa4e15f998b92f5507b644d8ffc3788afc8e64423c612e0785a6a92b74782ce368f49a6746084b50d874 + languageName: node + linkType: hard + +"@npmcli/fs@npm:^3.1.0": + version: 3.1.1 + resolution: "@npmcli/fs@npm:3.1.1" + dependencies: + semver: ^7.3.5 + checksum: d960cab4b93adcb31ce223bfb75c5714edbd55747342efb67dcc2f25e023d930a7af6ece3e75f2f459b6f38fc14d031c766f116cd124fdc937fd33112579e820 + languageName: node + linkType: hard + +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 6ad6a00fc4f2f2cfc6bff76fb1d88b8ee20bc0601e18ebb01b6d4be583733a860239a521a7fbca73b612e66705078809483549d2b18f370eb346c5155c8e4a0f + languageName: node + linkType: hard + +"@playwright/test@npm:^1.45.1": + version: 1.45.1 + resolution: "@playwright/test@npm:1.45.1" + dependencies: + playwright: 1.45.1 + bin: + playwright: cli.js + checksum: 43ae29946d44adc6d855af233cfa59dd145b116b9341fb7758c723ec029d506982e69e230bdd06291bee2d373a53de1833cf5279cf33bef74efcd0f1ea90fd34 + languageName: node + linkType: hard + +"@tenkeylabs/dappwright@npm:^2.8.5": + version: 2.8.5 + resolution: "@tenkeylabs/dappwright@npm:2.8.5" + dependencies: + node-stream-zip: ^1.13.0 + peerDependencies: + playwright-core: ">1.0" + checksum: 901a5984a96f90841282a3081285e60dc6007ffb0b25492a360fb46562a5f863b34dc162893e662d0dd99d2571d964e360c416823d956f41603c9a6fa074f388 + languageName: node + linkType: hard + +"@types/node@npm:^20.14.11": + version: 20.14.11 + resolution: "@types/node@npm:20.14.11" + dependencies: + undici-types: ~5.26.4 + checksum: 24396dea2bc803c2d2ebfdd31a3e6e93818ba1a5933d63cd0f64fad1e2955a8280ba09338a48ffe68cd84748eec8bee27135045f15661aa389656f67fe0b0924 + languageName: node + linkType: hard + +"abbrev@npm:^2.0.0": + version: 2.0.0 + resolution: "abbrev@npm:2.0.0" + checksum: 0e994ad2aa6575f94670d8a2149afe94465de9cedaaaac364e7fb43a40c3691c980ff74899f682f4ca58fa96b4cbd7421a015d3a6defe43a442117d7821a2f36 + languageName: node + linkType: hard + +"aes-js@npm:3.0.0": + version: 3.0.0 + resolution: "aes-js@npm:3.0.0" + checksum: 251e26d533cd1a915b44896b17d5ed68c24a02484cfdd2e74ec700a309267db96651ea4eb657bf20aac32a3baa61f6e34edf8e2fec2de440a655da9942d334b8 + languageName: node + linkType: hard + +"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1": + version: 7.1.1 + resolution: "agent-base@npm:7.1.1" + dependencies: + debug: ^4.3.4 + checksum: 51c158769c5c051482f9ca2e6e1ec085ac72b5a418a9b31b4e82fe6c0a6699adb94c1c42d246699a587b3335215037091c79e0de512c516f73b6ea844202f037 + languageName: node + linkType: hard + +"aggregate-error@npm:^3.0.0": + version: 3.1.0 + resolution: "aggregate-error@npm:3.1.0" + dependencies: + clean-stack: ^2.0.0 + indent-string: ^4.0.0 + checksum: 1101a33f21baa27a2fa8e04b698271e64616b886795fd43c31068c07533c7b3facfcaf4e9e0cab3624bd88f729a592f1c901a1a229c9e490eafce411a8644b79 + languageName: node + linkType: hard + +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 2aa4bb54caf2d622f1afdad09441695af2a83aa3fe8b8afa581d205e57ed4261c183c4d3877cee25794443fde5876417d859c108078ab788d6af7e4fe52eb66b + languageName: node + linkType: hard + +"ansi-regex@npm:^6.0.1": + version: 6.0.1 + resolution: "ansi-regex@npm:6.0.1" + checksum: 1ff8b7667cded1de4fa2c9ae283e979fc87036864317da86a2e546725f96406746411d0d85e87a2d12fa5abd715d90006de7fa4fa0477c92321ad3b4c7d4e169 + languageName: node + linkType: hard + +"ansi-styles@npm:^4.0.0": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" + dependencies: + color-convert: ^2.0.1 + checksum: 513b44c3b2105dd14cc42a19271e80f386466c4be574bccf60b627432f9198571ebf4ab1e4c3ba17347658f4ee1711c163d574248c0c1cdc2d5917a0ad582ec4 + languageName: node + linkType: hard + +"ansi-styles@npm:^6.1.0": + version: 6.2.1 + resolution: "ansi-styles@npm:6.2.1" + checksum: ef940f2f0ced1a6347398da88a91da7930c33ecac3c77b72c5905f8b8fe402c52e6fde304ff5347f616e27a742da3f1dc76de98f6866c69251ad0b07a66776d9 + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65 + languageName: node + linkType: hard + +"base-x@npm:^4.0.0": + version: 4.0.0 + resolution: "base-x@npm:4.0.0" + checksum: b25db9e07eb1998472a20557c7f00c797dc0595f79df95155ab74274e7fa98b9f2659b3ee547ac8773666b7f69540656793aeb97ad2b1ceccdb6fa5faaf69ac0 + languageName: node + linkType: hard + +"bech32@npm:1.1.4": + version: 1.1.4 + resolution: "bech32@npm:1.1.4" + checksum: 0e98db619191548390d6f09ff68b0253ba7ae6a55db93dfdbb070ba234c1fd3308c0606fbcc95fad50437227b10011e2698b89f0181f6e7f845c499bd14d0f4b + languageName: node + linkType: hard + +"bn.js@npm:^4.11.9": + version: 4.12.0 + resolution: "bn.js@npm:4.12.0" + checksum: 39afb4f15f4ea537b55eaf1446c896af28ac948fdcf47171961475724d1bb65118cca49fa6e3d67706e4790955ec0e74de584e45c8f1ef89f46c812bee5b5a12 + languageName: node + linkType: hard + +"bn.js@npm:^5.2.1": + version: 5.2.1 + resolution: "bn.js@npm:5.2.1" + checksum: 3dd8c8d38055fedfa95c1d5fc3c99f8dd547b36287b37768db0abab3c239711f88ff58d18d155dd8ad902b0b0cee973747b7ae20ea12a09473272b0201c9edd3 + languageName: node + linkType: hard + +"brace-expansion@npm:^2.0.1": + version: 2.0.1 + resolution: "brace-expansion@npm:2.0.1" + dependencies: + balanced-match: ^1.0.0 + checksum: a61e7cd2e8a8505e9f0036b3b6108ba5e926b4b55089eeb5550cd04a471fe216c96d4fe7e4c7f995c728c554ae20ddfc4244cad10aef255e72b62930afd233d1 + languageName: node + linkType: hard + +"brorand@npm:^1.1.0": + version: 1.1.0 + resolution: "brorand@npm:1.1.0" + checksum: 8a05c9f3c4b46572dec6ef71012b1946db6cae8c7bb60ccd4b7dd5a84655db49fe043ecc6272e7ef1f69dc53d6730b9e2a3a03a8310509a3d797a618cbee52be + languageName: node + linkType: hard + +"bs58@npm:^5.0.0": + version: 5.0.0 + resolution: "bs58@npm:5.0.0" + dependencies: + base-x: ^4.0.0 + checksum: 2475cb0684e07077521aac718e604a13e0f891d58cff923d437a2f7e9e28703ab39fce9f84c7c703ab369815a675f11e3bd394d38643bfe8969fbe42e6833d45 + languageName: node + linkType: hard + +"cacache@npm:^18.0.0": + version: 18.0.4 + resolution: "cacache@npm:18.0.4" + dependencies: + "@npmcli/fs": ^3.1.0 + fs-minipass: ^3.0.0 + glob: ^10.2.2 + lru-cache: ^10.0.1 + minipass: ^7.0.3 + minipass-collect: ^2.0.1 + minipass-flush: ^1.0.5 + minipass-pipeline: ^1.2.4 + p-map: ^4.0.0 + ssri: ^10.0.0 + tar: ^6.1.11 + unique-filename: ^3.0.0 + checksum: b7422c113b4ec750f33beeca0f426a0024c28e3172f332218f48f963e5b970647fa1ac05679fe5bb448832c51efea9fda4456b9a95c3a1af1105fe6c1833cde2 + languageName: node + linkType: hard + +"chownr@npm:^2.0.0": + version: 2.0.0 + resolution: "chownr@npm:2.0.0" + checksum: c57cf9dd0791e2f18a5ee9c1a299ae6e801ff58fee96dc8bfd0dcb4738a6ce58dd252a3605b1c93c6418fe4f9d5093b28ffbf4d66648cb2a9c67eaef9679be2f + languageName: node + linkType: hard + +"clean-stack@npm:^2.0.0": + version: 2.2.0 + resolution: "clean-stack@npm:2.2.0" + checksum: 2ac8cd2b2f5ec986a3c743935ec85b07bc174d5421a5efc8017e1f146a1cf5f781ae962618f416352103b32c9cd7e203276e8c28241bbe946160cab16149fb68 + languageName: node + linkType: hard + +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" + dependencies: + color-name: ~1.1.4 + checksum: 79e6bdb9fd479a205c71d89574fccfb22bd9053bd98c6c4d870d65c132e5e904e6034978e55b43d69fcaa7433af2016ee203ce76eeba9cfa554b373e7f7db336 + languageName: node + linkType: hard + +"color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 + languageName: node + linkType: hard + +"cross-spawn@npm:^7.0.0": + version: 7.0.3 + resolution: "cross-spawn@npm:7.0.3" + dependencies: + path-key: ^3.1.0 + shebang-command: ^2.0.0 + which: ^2.0.1 + checksum: 671cc7c7288c3a8406f3c69a3ae2fc85555c04169e9d611def9a675635472614f1c0ed0ef80955d5b6d4e724f6ced67f0ad1bb006c2ea643488fcfef994d7f52 + languageName: node + linkType: hard + +"debug@npm:4, debug@npm:^4.3.4": + version: 4.3.6 + resolution: "debug@npm:4.3.6" + dependencies: + ms: 2.1.2 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 1630b748dea3c581295e02137a9f5cbe2c1d85fea35c1e6597a65ca2b16a6fce68cec61b299d480787ef310ba927dc8c92d3061faba0ad06c6a724672f66be7f + languageName: node + linkType: hard + +"dotenv@npm:16.4.5": + version: 16.4.5 + resolution: "dotenv@npm:16.4.5" + checksum: 301a12c3d44fd49888b74eb9ccf9f07a1f5df43f489e7fcb89647a2edcd84c42d6bc349dc8df099cd18f07c35c7b04685c1a4f3e6a6a9e6b30f8d48c15b7f49c + languageName: node + linkType: hard + +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 7d00d7cd8e49b9afa762a813faac332dee781932d6f2c848dc348939c4253f1d4564341b7af1d041853bc3f32c2ef141b58e0a4d9862c17a7f08f68df1e0f1ed + languageName: node + linkType: hard + +"elliptic@npm:6.5.4": + version: 6.5.4 + resolution: "elliptic@npm:6.5.4" + dependencies: + bn.js: ^4.11.9 + brorand: ^1.1.0 + hash.js: ^1.0.0 + hmac-drbg: ^1.0.1 + inherits: ^2.0.4 + minimalistic-assert: ^1.0.1 + minimalistic-crypto-utils: ^1.0.1 + checksum: d56d21fd04e97869f7ffcc92e18903b9f67f2d4637a23c860492fbbff5a3155fd9ca0184ce0c865dd6eb2487d234ce9551335c021c376cd2d3b7cb749c7d10f4 + languageName: node + linkType: hard + +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: d4c5c39d5a9868b5fa152f00cada8a936868fd3367f33f71be515ecee4c803132d11b31a6222b2571b1e5f7e13890156a94880345594d0ce7e3c9895f560f192 + languageName: node + linkType: hard + +"emoji-regex@npm:^9.2.2": + version: 9.2.2 + resolution: "emoji-regex@npm:9.2.2" + checksum: 8487182da74aabd810ac6d6f1994111dfc0e331b01271ae01ec1eb0ad7b5ecc2bbbbd2f053c05cb55a1ac30449527d819bbfbf0e3de1023db308cbcb47f86601 + languageName: node + linkType: hard + +"encoding@npm:^0.1.13": + version: 0.1.13 + resolution: "encoding@npm:0.1.13" + dependencies: + iconv-lite: ^0.6.2 + checksum: bb98632f8ffa823996e508ce6a58ffcf5856330fde839ae42c9e1f436cc3b5cc651d4aeae72222916545428e54fd0f6aa8862fd8d25bdbcc4589f1e3f3715e7f + languageName: node + linkType: hard + +"env-paths@npm:^2.2.0": + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 65b5df55a8bab92229ab2b40dad3b387fad24613263d103a97f91c9fe43ceb21965cd3392b1ccb5d77088021e525c4e0481adb309625d0cb94ade1d1fb8dc17e + languageName: node + linkType: hard + +"err-code@npm:^2.0.2": + version: 2.0.3 + resolution: "err-code@npm:2.0.3" + checksum: 8b7b1be20d2de12d2255c0bc2ca638b7af5171142693299416e6a9339bd7d88fc8d7707d913d78e0993176005405a236b066b45666b27b797252c771156ace54 + languageName: node + linkType: hard + +"ethers@npm:^5.7.1": + version: 5.7.2 + resolution: "ethers@npm:5.7.2" + dependencies: + "@ethersproject/abi": 5.7.0 + "@ethersproject/abstract-provider": 5.7.0 + "@ethersproject/abstract-signer": 5.7.0 + "@ethersproject/address": 5.7.0 + "@ethersproject/base64": 5.7.0 + "@ethersproject/basex": 5.7.0 + "@ethersproject/bignumber": 5.7.0 + "@ethersproject/bytes": 5.7.0 + "@ethersproject/constants": 5.7.0 + "@ethersproject/contracts": 5.7.0 + "@ethersproject/hash": 5.7.0 + "@ethersproject/hdnode": 5.7.0 + "@ethersproject/json-wallets": 5.7.0 + "@ethersproject/keccak256": 5.7.0 + "@ethersproject/logger": 5.7.0 + "@ethersproject/networks": 5.7.1 + "@ethersproject/pbkdf2": 5.7.0 + "@ethersproject/properties": 5.7.0 + "@ethersproject/providers": 5.7.2 + "@ethersproject/random": 5.7.0 + "@ethersproject/rlp": 5.7.0 + "@ethersproject/sha2": 5.7.0 + "@ethersproject/signing-key": 5.7.0 + "@ethersproject/solidity": 5.7.0 + "@ethersproject/strings": 5.7.0 + "@ethersproject/transactions": 5.7.0 + "@ethersproject/units": 5.7.0 + "@ethersproject/wallet": 5.7.0 + "@ethersproject/web": 5.7.1 + "@ethersproject/wordlists": 5.7.0 + checksum: b7c08cf3e257185a7946117dbbf764433b7ba0e77c27298dec6088b3bc871aff711462b0621930c56880ff0a7ceb8b1d3a361ffa259f93377b48e34107f62553 + languageName: node + linkType: hard + +"exponential-backoff@npm:^3.1.1": + version: 3.1.1 + resolution: "exponential-backoff@npm:3.1.1" + checksum: 3d21519a4f8207c99f7457287291316306255a328770d320b401114ec8481986e4e467e854cb9914dd965e0a1ca810a23ccb559c642c88f4c7f55c55778a9b48 + languageName: node + linkType: hard + +"foreground-child@npm:^3.1.0": + version: 3.2.1 + resolution: "foreground-child@npm:3.2.1" + dependencies: + cross-spawn: ^7.0.0 + signal-exit: ^4.0.1 + checksum: 3e2e844d6003c96d70affe8ae98d7eaaba269a868c14d997620c088340a8775cd5d2d9043e6ceebae1928d8d9a874911c4d664b9a267e8995945df20337aebc0 + languageName: node + linkType: hard + +"fs-minipass@npm:^2.0.0": + version: 2.1.0 + resolution: "fs-minipass@npm:2.1.0" + dependencies: + minipass: ^3.0.0 + checksum: 1b8d128dae2ac6cc94230cc5ead341ba3e0efaef82dab46a33d171c044caaa6ca001364178d42069b2809c35a1c3c35079a32107c770e9ffab3901b59af8c8b1 + languageName: node + linkType: hard + +"fs-minipass@npm:^3.0.0": + version: 3.0.3 + resolution: "fs-minipass@npm:3.0.3" + dependencies: + minipass: ^7.0.3 + checksum: 8722a41109130851d979222d3ec88aabaceeaaf8f57b2a8f744ef8bd2d1ce95453b04a61daa0078822bc5cd21e008814f06fe6586f56fef511e71b8d2394d802 + languageName: node + linkType: hard + +"fsevents@npm:2.3.2": + version: 2.3.2 + resolution: "fsevents@npm:2.3.2" + dependencies: + node-gyp: latest + checksum: 97ade64e75091afee5265e6956cb72ba34db7819b4c3e94c431d4be2b19b8bb7a2d4116da417950c3425f17c8fe693d25e20212cac583ac1521ad066b77ae31f + conditions: os=darwin + languageName: node + linkType: hard + +"fsevents@patch:fsevents@2.3.2#~builtin": + version: 2.3.2 + resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=18f3a7" + dependencies: + node-gyp: latest + conditions: os=darwin + languageName: node + linkType: hard + +"glob@npm:^10.2.2, glob@npm:^10.3.10": + version: 10.4.5 + resolution: "glob@npm:10.4.5" + dependencies: + foreground-child: ^3.1.0 + jackspeak: ^3.1.2 + minimatch: ^9.0.4 + minipass: ^7.1.2 + package-json-from-dist: ^1.0.0 + path-scurry: ^1.11.1 + bin: + glob: dist/esm/bin.mjs + checksum: 0bc725de5e4862f9f387fd0f2b274baf16850dcd2714502ccf471ee401803997983e2c05590cb65f9675a3c6f2a58e7a53f9e365704108c6ad3cbf1d60934c4a + languageName: node + linkType: hard + +"gmx-ui-autotests@workspace:.": + version: 0.0.0-use.local + resolution: "gmx-ui-autotests@workspace:." + dependencies: + "@depay/web3-mock": ^14.18.0 + "@playwright/test": ^1.45.1 + "@tenkeylabs/dappwright": ^2.8.5 + "@types/node": ^20.14.11 + dotenv: 16.4.5 + languageName: unknown + linkType: soft + +"graceful-fs@npm:^4.2.6": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: ac85f94da92d8eb6b7f5a8b20ce65e43d66761c55ce85ac96df6865308390da45a8d3f0296dd3a663de65d30ba497bd46c696cc1e248c72b13d6d567138a4fc7 + languageName: node + linkType: hard + +"hash.js@npm:1.1.7, hash.js@npm:^1.0.0, hash.js@npm:^1.0.3": + version: 1.1.7 + resolution: "hash.js@npm:1.1.7" + dependencies: + inherits: ^2.0.3 + minimalistic-assert: ^1.0.1 + checksum: e350096e659c62422b85fa508e4b3669017311aa4c49b74f19f8e1bc7f3a54a584fdfd45326d4964d6011f2b2d882e38bea775a96046f2a61b7779a979629d8f + languageName: node + linkType: hard + +"hmac-drbg@npm:^1.0.1": + version: 1.0.1 + resolution: "hmac-drbg@npm:1.0.1" + dependencies: + hash.js: ^1.0.3 + minimalistic-assert: ^1.0.0 + minimalistic-crypto-utils: ^1.0.1 + checksum: bd30b6a68d7f22d63f10e1888aee497d7c2c5c0bb469e66bbdac99f143904d1dfe95f8131f95b3e86c86dd239963c9d972fcbe147e7cffa00e55d18585c43fe0 + languageName: node + linkType: hard + +"http-cache-semantics@npm:^4.1.1": + version: 4.1.1 + resolution: "http-cache-semantics@npm:4.1.1" + checksum: 83ac0bc60b17a3a36f9953e7be55e5c8f41acc61b22583060e8dedc9dd5e3607c823a88d0926f9150e571f90946835c7fe150732801010845c72cd8bbff1a236 + languageName: node + linkType: hard + +"http-proxy-agent@npm:^7.0.0": + version: 7.0.2 + resolution: "http-proxy-agent@npm:7.0.2" + dependencies: + agent-base: ^7.1.0 + debug: ^4.3.4 + checksum: 670858c8f8f3146db5889e1fa117630910101db601fff7d5a8aa637da0abedf68c899f03d3451cac2f83bcc4c3d2dabf339b3aa00ff8080571cceb02c3ce02f3 + languageName: node + linkType: hard + +"https-proxy-agent@npm:^7.0.1": + version: 7.0.5 + resolution: "https-proxy-agent@npm:7.0.5" + dependencies: + agent-base: ^7.0.2 + debug: 4 + checksum: 2e1a28960f13b041a50702ee74f240add8e75146a5c37fc98f1960f0496710f6918b3a9fe1e5aba41e50f58e6df48d107edd9c405c5f0d73ac260dabf2210857 + languageName: node + linkType: hard + +"iconv-lite@npm:^0.6.2": + version: 0.6.3 + resolution: "iconv-lite@npm:0.6.3" + dependencies: + safer-buffer: ">= 2.1.2 < 3.0.0" + checksum: 3f60d47a5c8fc3313317edfd29a00a692cc87a19cac0159e2ce711d0ebc9019064108323b5e493625e25594f11c6236647d8e256fbe7a58f4a3b33b89e6d30bf + languageName: node + linkType: hard + +"imurmurhash@npm:^0.1.4": + version: 0.1.4 + resolution: "imurmurhash@npm:0.1.4" + checksum: 7cae75c8cd9a50f57dadd77482359f659eaebac0319dd9368bcd1714f55e65badd6929ca58569da2b6494ef13fdd5598cd700b1eba23f8b79c5f19d195a3ecf7 + languageName: node + linkType: hard + +"indent-string@npm:^4.0.0": + version: 4.0.0 + resolution: "indent-string@npm:4.0.0" + checksum: 824cfb9929d031dabf059bebfe08cf3137365e112019086ed3dcff6a0a7b698cb80cf67ccccde0e25b9e2d7527aa6cc1fed1ac490c752162496caba3e6699612 + languageName: node + linkType: hard + +"inherits@npm:^2.0.3, inherits@npm:^2.0.4": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 + languageName: node + linkType: hard + +"ip-address@npm:^9.0.5": + version: 9.0.5 + resolution: "ip-address@npm:9.0.5" + dependencies: + jsbn: 1.1.0 + sprintf-js: ^1.1.3 + checksum: aa15f12cfd0ef5e38349744e3654bae649a34c3b10c77a674a167e99925d1549486c5b14730eebce9fea26f6db9d5e42097b00aa4f9f612e68c79121c71652dc + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 44a30c29457c7fb8f00297bce733f0a64cd22eca270f83e58c105e0d015e45c019491a4ab2faef91ab51d4738c670daff901c799f6a700e27f7314029e99e348 + languageName: node + linkType: hard + +"is-lambda@npm:^1.0.1": + version: 1.0.1 + resolution: "is-lambda@npm:1.0.1" + checksum: 93a32f01940220532e5948538699ad610d5924ac86093fcee83022252b363eb0cc99ba53ab084a04e4fb62bf7b5731f55496257a4c38adf87af9c4d352c71c35 + languageName: node + linkType: hard + +"isexe@npm:^2.0.0": + version: 2.0.0 + resolution: "isexe@npm:2.0.0" + checksum: 26bf6c5480dda5161c820c5b5c751ae1e766c587b1f951ea3fcfc973bafb7831ae5b54a31a69bd670220e42e99ec154475025a468eae58ea262f813fdc8d1c62 + languageName: node + linkType: hard + +"isexe@npm:^3.1.1": + version: 3.1.1 + resolution: "isexe@npm:3.1.1" + checksum: 7fe1931ee4e88eb5aa524cd3ceb8c882537bc3a81b02e438b240e47012eef49c86904d0f0e593ea7c3a9996d18d0f1f3be8d3eaa92333977b0c3a9d353d5563e + languageName: node + linkType: hard + +"jackspeak@npm:^3.1.2": + version: 3.4.3 + resolution: "jackspeak@npm:3.4.3" + dependencies: + "@isaacs/cliui": ^8.0.2 + "@pkgjs/parseargs": ^0.11.0 + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: be31027fc72e7cc726206b9f560395604b82e0fddb46c4cbf9f97d049bcef607491a5afc0699612eaa4213ca5be8fd3e1e7cd187b3040988b65c9489838a7c00 + languageName: node + linkType: hard + +"js-sha3@npm:0.8.0": + version: 0.8.0 + resolution: "js-sha3@npm:0.8.0" + checksum: 75df77c1fc266973f06cce8309ce010e9e9f07ec35ab12022ed29b7f0d9c8757f5a73e1b35aa24840dced0dea7059085aa143d817aea9e188e2a80d569d9adce + languageName: node + linkType: hard + +"jsbn@npm:1.1.0": + version: 1.1.0 + resolution: "jsbn@npm:1.1.0" + checksum: 944f924f2bd67ad533b3850eee47603eed0f6ae425fd1ee8c760f477e8c34a05f144c1bd4f5a5dd1963141dc79a2c55f89ccc5ab77d039e7077f3ad196b64965 + languageName: node + linkType: hard + +"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": + version: 10.4.3 + resolution: "lru-cache@npm:10.4.3" + checksum: 6476138d2125387a6d20f100608c2583d415a4f64a0fecf30c9e2dda976614f09cad4baa0842447bd37dd459a7bd27f57d9d8f8ce558805abd487c583f3d774a + languageName: node + linkType: hard + +"make-fetch-happen@npm:^13.0.0": + version: 13.0.1 + resolution: "make-fetch-happen@npm:13.0.1" + dependencies: + "@npmcli/agent": ^2.0.0 + cacache: ^18.0.0 + http-cache-semantics: ^4.1.1 + is-lambda: ^1.0.1 + minipass: ^7.0.2 + minipass-fetch: ^3.0.0 + minipass-flush: ^1.0.5 + minipass-pipeline: ^1.2.4 + negotiator: ^0.6.3 + proc-log: ^4.2.0 + promise-retry: ^2.0.1 + ssri: ^10.0.0 + checksum: 5c9fad695579b79488fa100da05777213dd9365222f85e4757630f8dd2a21a79ddd3206c78cfd6f9b37346819681782b67900ac847a57cf04190f52dda5343fd + languageName: node + linkType: hard + +"minimalistic-assert@npm:^1.0.0, minimalistic-assert@npm:^1.0.1": + version: 1.0.1 + resolution: "minimalistic-assert@npm:1.0.1" + checksum: cc7974a9268fbf130fb055aff76700d7e2d8be5f761fb5c60318d0ed010d839ab3661a533ad29a5d37653133385204c503bfac995aaa4236f4e847461ea32ba7 + languageName: node + linkType: hard + +"minimalistic-crypto-utils@npm:^1.0.1": + version: 1.0.1 + resolution: "minimalistic-crypto-utils@npm:1.0.1" + checksum: 6e8a0422b30039406efd4c440829ea8f988845db02a3299f372fceba56ffa94994a9c0f2fd70c17f9969eedfbd72f34b5070ead9656a34d3f71c0bd72583a0ed + languageName: node + linkType: hard + +"minimatch@npm:^9.0.4": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" + dependencies: + brace-expansion: ^2.0.1 + checksum: 2c035575eda1e50623c731ec6c14f65a85296268f749b9337005210bb2b34e2705f8ef1a358b188f69892286ab99dc42c8fb98a57bde55c8d81b3023c19cea28 + languageName: node + linkType: hard + +"minipass-collect@npm:^2.0.1": + version: 2.0.1 + resolution: "minipass-collect@npm:2.0.1" + dependencies: + minipass: ^7.0.3 + checksum: b251bceea62090f67a6cced7a446a36f4cd61ee2d5cea9aee7fff79ba8030e416327a1c5aa2908dc22629d06214b46d88fdab8c51ac76bacbf5703851b5ad342 + languageName: node + linkType: hard + +"minipass-fetch@npm:^3.0.0": + version: 3.0.5 + resolution: "minipass-fetch@npm:3.0.5" + dependencies: + encoding: ^0.1.13 + minipass: ^7.0.3 + minipass-sized: ^1.0.3 + minizlib: ^2.1.2 + dependenciesMeta: + encoding: + optional: true + checksum: 8047d273236157aab27ab7cd8eab7ea79e6ecd63e8f80c3366ec076cb9a0fed550a6935bab51764369027c414647fd8256c2a20c5445fb250c483de43350de83 + languageName: node + linkType: hard + +"minipass-flush@npm:^1.0.5": + version: 1.0.5 + resolution: "minipass-flush@npm:1.0.5" + dependencies: + minipass: ^3.0.0 + checksum: 56269a0b22bad756a08a94b1ffc36b7c9c5de0735a4dd1ab2b06c066d795cfd1f0ac44a0fcae13eece5589b908ecddc867f04c745c7009be0b566421ea0944cf + languageName: node + linkType: hard + +"minipass-pipeline@npm:^1.2.4": + version: 1.2.4 + resolution: "minipass-pipeline@npm:1.2.4" + dependencies: + minipass: ^3.0.0 + checksum: b14240dac0d29823c3d5911c286069e36d0b81173d7bdf07a7e4a91ecdef92cdff4baaf31ea3746f1c61e0957f652e641223970870e2353593f382112257971b + languageName: node + linkType: hard + +"minipass-sized@npm:^1.0.3": + version: 1.0.3 + resolution: "minipass-sized@npm:1.0.3" + dependencies: + minipass: ^3.0.0 + checksum: 79076749fcacf21b5d16dd596d32c3b6bf4d6e62abb43868fac21674078505c8b15eaca4e47ed844985a4514854f917d78f588fcd029693709417d8f98b2bd60 + languageName: node + linkType: hard + +"minipass@npm:^3.0.0": + version: 3.3.6 + resolution: "minipass@npm:3.3.6" + dependencies: + yallist: ^4.0.0 + checksum: a30d083c8054cee83cdcdc97f97e4641a3f58ae743970457b1489ce38ee1167b3aaf7d815cd39ec7a99b9c40397fd4f686e83750e73e652b21cb516f6d845e48 + languageName: node + linkType: hard + +"minipass@npm:^5.0.0": + version: 5.0.0 + resolution: "minipass@npm:5.0.0" + checksum: 425dab288738853fded43da3314a0b5c035844d6f3097a8e3b5b29b328da8f3c1af6fc70618b32c29ff906284cf6406b6841376f21caaadd0793c1d5a6a620ea + languageName: node + linkType: hard + +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.1.2": + version: 7.1.2 + resolution: "minipass@npm:7.1.2" + checksum: 2bfd325b95c555f2b4d2814d49325691c7bee937d753814861b0b49d5edcda55cbbf22b6b6a60bb91eddac8668771f03c5ff647dcd9d0f798e9548b9cdc46ee3 + languageName: node + linkType: hard + +"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": + version: 2.1.2 + resolution: "minizlib@npm:2.1.2" + dependencies: + minipass: ^3.0.0 + yallist: ^4.0.0 + checksum: f1fdeac0b07cf8f30fcf12f4b586795b97be856edea22b5e9072707be51fc95d41487faec3f265b42973a304fe3a64acd91a44a3826a963e37b37bafde0212c3 + languageName: node + linkType: hard + +"mkdirp@npm:^1.0.3": + version: 1.0.4 + resolution: "mkdirp@npm:1.0.4" + bin: + mkdirp: bin/cmd.js + checksum: a96865108c6c3b1b8e1d5e9f11843de1e077e57737602de1b82030815f311be11f96f09cce59bd5b903d0b29834733e5313f9301e3ed6d6f6fba2eae0df4298f + languageName: node + linkType: hard + +"ms@npm:2.1.2": + version: 2.1.2 + resolution: "ms@npm:2.1.2" + checksum: 673cdb2c3133eb050c745908d8ce632ed2c02d85640e2edb3ace856a2266a813b30c613569bf3354fdf4ea7d1a1494add3bfa95e2713baa27d0c2c71fc44f58f + languageName: node + linkType: hard + +"negotiator@npm:^0.6.3": + version: 0.6.3 + resolution: "negotiator@npm:0.6.3" + checksum: b8ffeb1e262eff7968fc90a2b6767b04cfd9842582a9d0ece0af7049537266e7b2506dfb1d107a32f06dd849ab2aea834d5830f7f4d0e5cb7d36e1ae55d021d9 + languageName: node + linkType: hard + +"node-gyp@npm:latest": + version: 10.2.0 + resolution: "node-gyp@npm:10.2.0" + dependencies: + env-paths: ^2.2.0 + exponential-backoff: ^3.1.1 + glob: ^10.3.10 + graceful-fs: ^4.2.6 + make-fetch-happen: ^13.0.0 + nopt: ^7.0.0 + proc-log: ^4.1.0 + semver: ^7.3.5 + tar: ^6.2.1 + which: ^4.0.0 + bin: + node-gyp: bin/node-gyp.js + checksum: 0233759d8c19765f7fdc259a35eb046ad86c3d09e22f7384613ae2b89647dd27fcf833fdf5293d9335041e91f9b1c539494225959cdb312a5c8080b7534b926f + languageName: node + linkType: hard + +"node-stream-zip@npm:^1.13.0": + version: 1.15.0 + resolution: "node-stream-zip@npm:1.15.0" + checksum: 0b73ffbb09490e479c8f47038d7cba803e6242618fbc1b71c26782009d388742ed6fb5ce6e9d31f528b410249e7eb1c6e7534e9d3792a0cafd99813ac5a35107 + languageName: node + linkType: hard + +"nopt@npm:^7.0.0": + version: 7.2.1 + resolution: "nopt@npm:7.2.1" + dependencies: + abbrev: ^2.0.0 + bin: + nopt: bin/nopt.js + checksum: 6fa729cc77ce4162cfad8abbc9ba31d4a0ff6850c3af61d59b505653bef4781ec059f8890ecfe93ee8aa0c511093369cca88bfc998101616a2904e715bbbb7c9 + languageName: node + linkType: hard + +"p-map@npm:^4.0.0": + version: 4.0.0 + resolution: "p-map@npm:4.0.0" + dependencies: + aggregate-error: ^3.0.0 + checksum: cb0ab21ec0f32ddffd31dfc250e3afa61e103ef43d957cc45497afe37513634589316de4eb88abdfd969fe6410c22c0b93ab24328833b8eb1ccc087fc0442a1c + languageName: node + linkType: hard + +"package-json-from-dist@npm:^1.0.0": + version: 1.0.0 + resolution: "package-json-from-dist@npm:1.0.0" + checksum: ac706ec856a5a03f5261e4e48fa974f24feb044d51f84f8332e2af0af04fbdbdd5bbbfb9cbbe354190409bc8307c83a9e38c6672c3c8855f709afb0006a009ea + languageName: node + linkType: hard + +"path-key@npm:^3.1.0": + version: 3.1.1 + resolution: "path-key@npm:3.1.1" + checksum: 55cd7a9dd4b343412a8386a743f9c746ef196e57c823d90ca3ab917f90ab9f13dd0ded27252ba49dbdfcab2b091d998bc446f6220cd3cea65db407502a740020 + languageName: node + linkType: hard + +"path-scurry@npm:^1.11.1": + version: 1.11.1 + resolution: "path-scurry@npm:1.11.1" + dependencies: + lru-cache: ^10.2.0 + minipass: ^5.0.0 || ^6.0.2 || ^7.0.0 + checksum: 890d5abcd593a7912dcce7cf7c6bf7a0b5648e3dee6caf0712c126ca0a65c7f3d7b9d769072a4d1baf370f61ce493ab5b038d59988688e0c5f3f646ee3c69023 + languageName: node + linkType: hard + +"playwright-core@npm:1.45.1": + version: 1.45.1 + resolution: "playwright-core@npm:1.45.1" + bin: + playwright-core: cli.js + checksum: 1279cbf33d70a16f0451b590723a4b56bf3a96a9196f1d7bd76444fdd8c976bb7671698199ab4e0acaeb800c7aac48a62036e30f27fd37a74925b53b80f2e85f + languageName: node + linkType: hard + +"playwright@npm:1.45.1": + version: 1.45.1 + resolution: "playwright@npm:1.45.1" + dependencies: + fsevents: 2.3.2 + playwright-core: 1.45.1 + dependenciesMeta: + fsevents: + optional: true + bin: + playwright: cli.js + checksum: 34c589b3e7ef4d54782336167f8f3d1b456178b04b98eb8c944eaf21fd66cf5480e5f0c0ec8b846a69659b6fc31764283598aac5bb9307f7413f8fca81d40751 + languageName: node + linkType: hard + +"proc-log@npm:^4.1.0, proc-log@npm:^4.2.0": + version: 4.2.0 + resolution: "proc-log@npm:4.2.0" + checksum: 98f6cd012d54b5334144c5255ecb941ee171744f45fca8b43b58ae5a0c1af07352475f481cadd9848e7f0250376ee584f6aa0951a856ff8f021bdfbff4eb33fc + languageName: node + linkType: hard + +"promise-retry@npm:^2.0.1": + version: 2.0.1 + resolution: "promise-retry@npm:2.0.1" + dependencies: + err-code: ^2.0.2 + retry: ^0.12.0 + checksum: f96a3f6d90b92b568a26f71e966cbbc0f63ab85ea6ff6c81284dc869b41510e6cdef99b6b65f9030f0db422bf7c96652a3fff9f2e8fb4a0f069d8f4430359429 + languageName: node + linkType: hard + +"retry@npm:^0.12.0": + version: 0.12.0 + resolution: "retry@npm:0.12.0" + checksum: 623bd7d2e5119467ba66202d733ec3c2e2e26568074923bc0585b6b99db14f357e79bdedb63cab56cec47491c4a0da7e6021a7465ca6dc4f481d3898fdd3158c + languageName: node + linkType: hard + +"safer-buffer@npm:>= 2.1.2 < 3.0.0": + version: 2.1.2 + resolution: "safer-buffer@npm:2.1.2" + checksum: cab8f25ae6f1434abee8d80023d7e72b598cf1327164ddab31003c51215526801e40b66c5e65d658a0af1e9d6478cadcb4c745f4bd6751f97d8644786c0978b0 + languageName: node + linkType: hard + +"scrypt-js@npm:3.0.1": + version: 3.0.1 + resolution: "scrypt-js@npm:3.0.1" + checksum: b7c7d1a68d6ca946f2fbb0778e0c4ec63c65501b54023b2af7d7e9f48fdb6c6580d6f7675cd53bda5944c5ebc057560d5a6365079752546865defb3b79dea454 + languageName: node + linkType: hard + +"semver@npm:^7.3.5": + version: 7.6.3 + resolution: "semver@npm:7.6.3" + bin: + semver: bin/semver.js + checksum: 4110ec5d015c9438f322257b1c51fe30276e5f766a3f64c09edd1d7ea7118ecbc3f379f3b69032bacf13116dc7abc4ad8ce0d7e2bd642e26b0d271b56b61a7d8 + languageName: node + linkType: hard + +"shebang-command@npm:^2.0.0": + version: 2.0.0 + resolution: "shebang-command@npm:2.0.0" + dependencies: + shebang-regex: ^3.0.0 + checksum: 6b52fe87271c12968f6a054e60f6bde5f0f3d2db483a1e5c3e12d657c488a15474121a1d55cd958f6df026a54374ec38a4a963988c213b7570e1d51575cea7fa + languageName: node + linkType: hard + +"shebang-regex@npm:^3.0.0": + version: 3.0.0 + resolution: "shebang-regex@npm:3.0.0" + checksum: 1a2bcae50de99034fcd92ad4212d8e01eedf52c7ec7830eedcf886622804fe36884278f2be8be0ea5fde3fd1c23911643a4e0f726c8685b61871c8908af01222 + languageName: node + linkType: hard + +"signal-exit@npm:^4.0.1": + version: 4.1.0 + resolution: "signal-exit@npm:4.1.0" + checksum: 64c757b498cb8629ffa5f75485340594d2f8189e9b08700e69199069c8e3070fb3e255f7ab873c05dc0b3cec412aea7402e10a5990cb6a050bd33ba062a6c549 + languageName: node + linkType: hard + +"smart-buffer@npm:^4.2.0": + version: 4.2.0 + resolution: "smart-buffer@npm:4.2.0" + checksum: b5167a7142c1da704c0e3af85c402002b597081dd9575031a90b4f229ca5678e9a36e8a374f1814c8156a725d17008ae3bde63b92f9cfd132526379e580bec8b + languageName: node + linkType: hard + +"socks-proxy-agent@npm:^8.0.3": + version: 8.0.4 + resolution: "socks-proxy-agent@npm:8.0.4" + dependencies: + agent-base: ^7.1.1 + debug: ^4.3.4 + socks: ^2.8.3 + checksum: b2ec5051d85fe49072f9a250c427e0e9571fd09d5db133819192d078fd291276e1f0f50f6dbc04329b207738b1071314cee8bdbb4b12e27de42dbcf1d4233c67 + languageName: node + linkType: hard + +"socks@npm:^2.8.3": + version: 2.8.3 + resolution: "socks@npm:2.8.3" + dependencies: + ip-address: ^9.0.5 + smart-buffer: ^4.2.0 + checksum: 7a6b7f6eedf7482b9e4597d9a20e09505824208006ea8f2c49b71657427f3c137ca2ae662089baa73e1971c62322d535d9d0cf1c9235cf6f55e315c18203eadd + languageName: node + linkType: hard + +"sprintf-js@npm:^1.1.3": + version: 1.1.3 + resolution: "sprintf-js@npm:1.1.3" + checksum: a3fdac7b49643875b70864a9d9b469d87a40dfeaf5d34d9d0c5b1cda5fd7d065531fcb43c76357d62254c57184a7b151954156563a4d6a747015cfb41021cad0 + languageName: node + linkType: hard + +"ssri@npm:^10.0.0": + version: 10.0.6 + resolution: "ssri@npm:10.0.6" + dependencies: + minipass: ^7.0.3 + checksum: 4603d53a05bcd44188747d38f1cc43833b9951b5a1ee43ba50535bdfc5fe4a0897472dbe69837570a5417c3c073377ef4f8c1a272683b401857f72738ee57299 + languageName: node + linkType: hard + +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: ^8.0.0 + is-fullwidth-code-point: ^3.0.0 + strip-ansi: ^6.0.1 + checksum: e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb + languageName: node + linkType: hard + +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: ^0.2.0 + emoji-regex: ^9.2.2 + strip-ansi: ^7.0.1 + checksum: 7369deaa29f21dda9a438686154b62c2c5f661f8dda60449088f9f980196f7908fc39fdd1803e3e01541970287cf5deae336798337e9319a7055af89dafa7193 + languageName: node + linkType: hard + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: ^5.0.1 + checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c + languageName: node + linkType: hard + +"strip-ansi@npm:^7.0.1": + version: 7.1.0 + resolution: "strip-ansi@npm:7.1.0" + dependencies: + ansi-regex: ^6.0.1 + checksum: 859c73fcf27869c22a4e4d8c6acfe690064659e84bef9458aa6d13719d09ca88dcfd40cbf31fd0be63518ea1a643fe070b4827d353e09533a5b0b9fd4553d64d + languageName: node + linkType: hard + +"tar@npm:^6.1.11, tar@npm:^6.2.1": + version: 6.2.1 + resolution: "tar@npm:6.2.1" + dependencies: + chownr: ^2.0.0 + fs-minipass: ^2.0.0 + minipass: ^5.0.0 + minizlib: ^2.1.1 + mkdirp: ^1.0.3 + yallist: ^4.0.0 + checksum: f1322768c9741a25356c11373bce918483f40fa9a25c69c59410c8a1247632487edef5fe76c5f12ac51a6356d2f1829e96d2bc34098668a2fc34d76050ac2b6c + languageName: node + linkType: hard + +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 3192ef6f3fd5df652f2dc1cd782b49d6ff14dc98e5dced492aa8a8c65425227da5da6aafe22523c67f035a272c599bb89cfe803c1db6311e44bed3042fc25487 + languageName: node + linkType: hard + +"unique-filename@npm:^3.0.0": + version: 3.0.0 + resolution: "unique-filename@npm:3.0.0" + dependencies: + unique-slug: ^4.0.0 + checksum: 8e2f59b356cb2e54aab14ff98a51ac6c45781d15ceaab6d4f1c2228b780193dc70fae4463ce9e1df4479cb9d3304d7c2043a3fb905bdeca71cc7e8ce27e063df + languageName: node + linkType: hard + +"unique-slug@npm:^4.0.0": + version: 4.0.0 + resolution: "unique-slug@npm:4.0.0" + dependencies: + imurmurhash: ^0.1.4 + checksum: 0884b58365af59f89739e6f71e3feacb5b1b41f2df2d842d0757933620e6de08eff347d27e9d499b43c40476cbaf7988638d3acb2ffbcb9d35fd035591adfd15 + languageName: node + linkType: hard + +"which@npm:^2.0.1": + version: 2.0.2 + resolution: "which@npm:2.0.2" + dependencies: + isexe: ^2.0.0 + bin: + node-which: ./bin/node-which + checksum: 1a5c563d3c1b52d5f893c8b61afe11abc3bab4afac492e8da5bde69d550de701cf9806235f20a47b5c8fa8a1d6a9135841de2596535e998027a54589000e66d1 + languageName: node + linkType: hard + +"which@npm:^4.0.0": + version: 4.0.0 + resolution: "which@npm:4.0.0" + dependencies: + isexe: ^3.1.1 + bin: + node-which: bin/which.js + checksum: f17e84c042592c21e23c8195108cff18c64050b9efb8459589116999ea9da6dd1509e6a1bac3aeebefd137be00fabbb61b5c2bc0aa0f8526f32b58ee2f545651 + languageName: node + linkType: hard + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: ^4.0.0 + string-width: ^4.1.0 + strip-ansi: ^6.0.0 + checksum: a790b846fd4505de962ba728a21aaeda189b8ee1c7568ca5e817d85930e06ef8d1689d49dbf0e881e8ef84436af3a88bc49115c2e2788d841ff1b8b5b51a608b + languageName: node + linkType: hard + +"wrap-ansi@npm:^8.1.0": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" + dependencies: + ansi-styles: ^6.1.0 + string-width: ^5.0.1 + strip-ansi: ^7.0.1 + checksum: 371733296dc2d616900ce15a0049dca0ef67597d6394c57347ba334393599e800bab03c41d4d45221b6bc967b8c453ec3ae4749eff3894202d16800fdfe0e238 + languageName: node + linkType: hard + +"ws@npm:7.4.6": + version: 7.4.6 + resolution: "ws@npm:7.4.6" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 3a990b32ed08c72070d5e8913e14dfcd831919205be52a3ff0b4cdd998c8d554f167c9df3841605cde8b11d607768cacab3e823c58c96a5c08c987e093eb767a + languageName: node + linkType: hard + +"yallist@npm:^4.0.0": + version: 4.0.0 + resolution: "yallist@npm:4.0.0" + checksum: 343617202af32df2a15a3be36a5a8c0c8545208f3d3dfbc6bb7c3e3b7e8c6f8e7485432e4f3b88da3031a6e20afa7c711eded32ddfb122896ac5d914e75848d5 + languageName: node + linkType: hard diff --git a/src/components/NetworkDropdown/NetworkDropdown.tsx b/src/components/NetworkDropdown/NetworkDropdown.tsx index d606f19e26..3aeff0548e 100644 --- a/src/components/NetworkDropdown/NetworkDropdown.tsx +++ b/src/components/NetworkDropdown/NetworkDropdown.tsx @@ -116,7 +116,7 @@ function DesktopDropdown({ setActiveModal, selectorLabel, networkOptions, openSe - +
Networks
@@ -189,7 +189,7 @@ function NetworkMenuItems({ networkOptions, selectorLabel }) { function NetworkModalContent({ networkOptions, selectorLabel, setActiveModal, openSettings }) { const { active } = useWallet(); return ( -
+
Networks diff --git a/src/pages/Buy/Buy.tsx b/src/pages/Buy/Buy.tsx index 88355a690c..8373d0a66d 100644 --- a/src/pages/Buy/Buy.tsx +++ b/src/pages/Buy/Buy.tsx @@ -12,10 +12,10 @@ import "./Buy.css"; export default function BuyGMXGLP() { return ( -
+
- +
diff --git a/src/pages/BuyGlp/BuyGlp.js b/src/pages/BuyGlp/BuyGlp.js index 16d8d5d230..1e0a614fd2 100644 --- a/src/pages/BuyGlp/BuyGlp.js +++ b/src/pages/BuyGlp/BuyGlp.js @@ -31,7 +31,7 @@ export default function BuyGlp() { {incentiveState?.migration?.isActive && ( diff --git a/src/pages/Dashboard/DashboardV2.tsx b/src/pages/Dashboard/DashboardV2.tsx index e868b41690..8c91e34b3e 100644 --- a/src/pages/Dashboard/DashboardV2.tsx +++ b/src/pages/Dashboard/DashboardV2.tsx @@ -578,7 +578,7 @@ export default function DashboardV2() { diff --git a/src/pages/Ecosystem/Ecosystem.js b/src/pages/Ecosystem/Ecosystem.js index e88d7ba6b2..7934a757ed 100644 --- a/src/pages/Ecosystem/Ecosystem.js +++ b/src/pages/Ecosystem/Ecosystem.js @@ -499,9 +499,9 @@ export default function Ecosystem() { return ( -
+
- +
{gmxPages.map((item) => { const linkLabel = item.linkLabel ? item.linkLabel : item.link; diff --git a/src/pages/LeaderboardPage/components/LeaderboardContainer.tsx b/src/pages/LeaderboardPage/components/LeaderboardContainer.tsx index 74d4aefbb3..702dd4e287 100644 --- a/src/pages/LeaderboardPage/components/LeaderboardContainer.tsx +++ b/src/pages/LeaderboardPage/components/LeaderboardContainer.tsx @@ -169,11 +169,11 @@ export function LeaderboardContainer() { }, [isMobile, leaderboardPageKey, wrongNetworkSwitcher]); return ( -
+
-

+

{title} {t`Chain

{description}
diff --git a/src/pages/MarketPoolsPage/MarketPoolsPage.tsx b/src/pages/MarketPoolsPage/MarketPoolsPage.tsx index 691a7e9836..411c2ce80d 100644 --- a/src/pages/MarketPoolsPage/MarketPoolsPage.tsx +++ b/src/pages/MarketPoolsPage/MarketPoolsPage.tsx @@ -68,6 +68,7 @@ export function MarketPoolsPage() { earn fees from swaps and leverage trading. } + qa="pools-page" />
diff --git a/src/pages/Referrals/Referrals.tsx b/src/pages/Referrals/Referrals.tsx index 2147f9f660..2b97aa085d 100644 --- a/src/pages/Referrals/Referrals.tsx +++ b/src/pages/Referrals/Referrals.tsx @@ -134,7 +134,7 @@ function Referrals() { referral program details. } - qa="referrals" + qa="referrals-page" />
From 06a794f2d86a831cd313f5d7337499cc98cbb882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hub=C3=A9rt=20de=20Lalye?= Date: Thu, 1 Aug 2024 10:56:02 +0400 Subject: [PATCH 05/62] fixed tests readme and file namings --- autotests/README.md | 13 +++++++++++++ autotests/src/base.ts | 16 +++++++++------- .../elements/{elements.ts => page-objects.ts} | 2 +- autotests/src/tests/trade.spec.ts | 11 ++++++++--- autotests/src/tests/wallet.spec.ts | 4 ++++ 5 files changed, 35 insertions(+), 11 deletions(-) rename autotests/src/elements/{elements.ts => page-objects.ts} (99%) diff --git a/autotests/README.md b/autotests/README.md index f8375bf940..3961edc395 100644 --- a/autotests/README.md +++ b/autotests/README.md @@ -12,6 +12,7 @@ This repository contains the autotests for the GMX interface. 1. Clone the repository to your local machine. 2. Install the dependencies by running `yarn`. +3. Install playwright dependencies by running `npx playwright install` ## How to run autotests locally @@ -27,3 +28,15 @@ NETWORK=arbitrum or fuji ``` 2. Run tests: `yarn test` + +## Writing tests + +Tests consist of test cases and page objects. The tests are designed to be simple, any complex interactions should be incapsulated in page objects. Each page object is either a [extended Locator](./src/elements/base-page.ts#L5) or inheritor of [BasePage](./src/elements/base-page.ts#12). Locators are extended interface to use only data-qa attributes for locating elements and implementing following API: + +- `.selector` - returns locator's selector string including data-qa attribute +- `.waitForSelector()` - waits for element to appear on page +- `.waitForVisible()` - waits for element to be visible + +BasePage is a class that provides basic methods for interacting with the page and manipulating locators. These classes are used to keep tests cases clean, declarative and simple, also to reuse same UI interactions in different pages. Each tests accepts [GmxApp object](./src/elements/page-objects.ts#L382) as a parameter, all interactions with APP should be accessible through this object. + +Ideally tests should be isolated from each other, so that they can be run in any order and not depend on each other. In other case use `test.describe.serial` to group tests that depend on each other. diff --git a/autotests/src/base.ts b/autotests/src/base.ts index 61a1ae869b..fd067231b6 100644 --- a/autotests/src/base.ts +++ b/autotests/src/base.ts @@ -1,6 +1,6 @@ import { BrowserContext, test as baseTest } from "@playwright/test"; import dappwright, { Dappwright, MetaMaskWallet } from "@tenkeylabs/dappwright"; -import { GmxPage } from "./elements/elements"; +import { GmxApp } from "./elements/page-objects"; import { config } from "dotenv"; import { mockWeb3 } from "./mocks/web3"; @@ -14,16 +14,19 @@ export const test = baseTest.extend<{ context: BrowserContext; wallet: Dappwright; appUrl: string; - gmx: GmxPage; + gmx: GmxApp; }>({ page: async ({ context }, use) => { const page = await context.newPage(); if (!process.env.USE_METAMASK) { + /** + * @todo Fix this mock to work with rainbowkit + */ await mockWeb3(page, () => { Web3Mock.mock({ blockchain: "ethereum", - accounts: { return: ["0xd73b04b0e696b0945283defa3eee453814758f1a"] }, + accounts: { return: [process.env.ACCOUNT] }, }); }); } @@ -54,11 +57,10 @@ export const test = baseTest.extend<{ await metamaskPage.locator('//button/h6[contains(text(), "Switch to Arbitrum One")]').click(); await metamaskPage.waitForTimeout(1000); await metamaskPage.locator('[data-testid="detected-token-banner"] button').click(); - await metamaskPage.waitForTimeout(2000); + await metamaskPage.waitForTimeout(1000); await metamaskPage .locator('//*[contains(@class, "popover-footer")]/button[contains(text(), "Import")]') .click(); - await metamaskPage.waitForTimeout(2000); } else { await wallet.addNetwork({ chainId: 43113, @@ -72,7 +74,7 @@ export const test = baseTest.extend<{ console.log("[Preparing GMX App]"); const page = await ctx.newPage(); await page.goto(appUrl); - const gmx = new GmxPage(page, wallet, appUrl); + const gmx = new GmxApp(page, wallet, appUrl); await gmx.header.connectWallet(); await gmx.closeAllToasts(); @@ -96,7 +98,7 @@ export const test = baseTest.extend<{ appUrl: async ({}, use) => use(process.env.GMX_BASE_URL || "https://app.gmx.io"), gmx: async ({ wallet, page, appUrl }, use) => { - const gmx = new GmxPage(page, wallet, appUrl); + const gmx = new GmxApp(page, wallet, appUrl); await use(gmx); }, }); diff --git a/autotests/src/elements/elements.ts b/autotests/src/elements/page-objects.ts similarity index 99% rename from autotests/src/elements/elements.ts rename to autotests/src/elements/page-objects.ts index 6a940f72f9..51e9122be1 100644 --- a/autotests/src/elements/elements.ts +++ b/autotests/src/elements/page-objects.ts @@ -379,7 +379,7 @@ class Position extends BasePage { } } -export class GmxPage extends BasePage { +export class GmxApp extends BasePage { header = new Header(this.page, this.wallet, this.locator("header")); tradebox = new Tradebox(this.page, this.wallet, this.locator("tradebox")); diff --git a/autotests/src/tests/trade.spec.ts b/autotests/src/tests/trade.spec.ts index d85def33e2..daa90a385a 100644 --- a/autotests/src/tests/trade.spec.ts +++ b/autotests/src/tests/trade.spec.ts @@ -27,7 +27,7 @@ test.describe.serial("Trades", () => { const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); await position.root.waitForSelector(); - expect(position.root).toBeVisible(); + expect(position.root).toBeAttached(); }); test("edit position deposit", async ({ page, gmx }) => { @@ -38,7 +38,7 @@ test.describe.serial("Trades", () => { expect(position.root).toBeVisible(); const collateral = await position.getCollateral(); - await position.deposit("1"); + await position.deposit("0.2"); const newCollateral = await position.getCollateral(); expect(newCollateral !== collateral).toBeTruthy(); @@ -74,6 +74,11 @@ test.describe.serial("Trades", () => { expect(position.root).toBeVisible(); await position.closeFull(); + + await position.root.waitFor({ + state: "detached", + }); + expect(position.root).not.toBeAttached(); }); }); @@ -107,7 +112,7 @@ test.describe.serial("Trades", () => { const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); await position.root.waitForSelector(); - expect(position.root).toBeVisible(); + expect(position.root).toBeAttached(); }); }); }); diff --git a/autotests/src/tests/wallet.spec.ts b/autotests/src/tests/wallet.spec.ts index ae34ca56b8..59d2b03b29 100644 --- a/autotests/src/tests/wallet.spec.ts +++ b/autotests/src/tests/wallet.spec.ts @@ -2,6 +2,10 @@ import { expect } from "@playwright/test"; import { test } from "../base"; test.describe("Wallet", () => { + test.afterEach(async ({ gmx }) => { + await gmx.page.close(); + }); + test("Should able to connect wallet", async ({ page, gmx }) => { await page.goto(gmx.baseUrl); await page.waitForSelector(gmx.header.userAddress.selector); From da6d1c9260241064ddf16b81cad9431f19365507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hub=C3=A9rt=20de=20Lalye?= Date: Tue, 6 Aug 2024 16:15:48 +0400 Subject: [PATCH 06/62] fixed autotests --- autotests/src/elements/base-page.ts | 26 ++++- autotests/src/elements/page-objects.ts | 97 +++++++++++++------ autotests/src/tests/trade.spec.ts | 31 ++++-- .../BuyInputSection/BuyInputSection.tsx | 2 +- src/components/Checkbox/Checkbox.tsx | 2 + src/components/Exchange/ExchangeInfoRow.tsx | 2 +- .../HighPriceImpactWarning.tsx | 2 + .../Synthetics/PositionItem/PositionItem.tsx | 4 +- .../PositionSeller/PositionSeller.tsx | 5 + .../Synthetics/SelectorBase/SelectorBase.tsx | 9 +- 10 files changed, 137 insertions(+), 43 deletions(-) diff --git a/autotests/src/elements/base-page.ts b/autotests/src/elements/base-page.ts index e10c7057ab..523920a2a7 100644 --- a/autotests/src/elements/base-page.ts +++ b/autotests/src/elements/base-page.ts @@ -6,6 +6,7 @@ declare module "@playwright/test" { selector: string; waitForVisible(): void; waitForSelector(): void; + waitForDetached(): void; } } @@ -25,6 +26,16 @@ export class BasePage { }); }; + locator.waitForDetached = async () => { + if (process.env.PWDEBUG) { + console.log(`Waiting for ${selector} to be detached`); + } + + await this.page.waitForSelector(selector, { + state: "detached", + }); + }; + locator.waitForSelector = async () => { if (process.env.PWDEBUG) { console.log(`Waiting for selector = ${selector}`); @@ -52,15 +63,26 @@ export class BasePage { } } - locator(selector: string) { + locator(selector: string, root?: Locator) { const locatorSelector = selector.startsWith(".") || selector.startsWith("//") || selector.startsWith("[") ? selector : `[data-qa="${selector}"]`; - const locator = this.root.locator(locatorSelector); + const locator = (root ?? this.root).locator(locatorSelector); return this.wrapLocatorWithRoot(locator, locatorSelector); } + async has(locator: string | Locator, timeout = 5000) { + const selector = typeof locator === "string" ? this.locator(locator).selector : locator.selector; + + try { + await this.page.waitForSelector(selector, { state: "attached", timeout }); + return true; + } catch (e) { + return false; + } + } + async waitForTransactionToBeSent() { /** * @todo Implement me! diff --git a/autotests/src/elements/page-objects.ts b/autotests/src/elements/page-objects.ts index 51e9122be1..d19c89057f 100644 --- a/autotests/src/elements/page-objects.ts +++ b/autotests/src/elements/page-objects.ts @@ -145,10 +145,17 @@ class Tradebox extends BasePage { } async selectPool(pool: string) { + const enabled = await this.has("pool-selector-button"); + + if (!enabled) { + console.log("[SelectPool]: pool selector is not enabled"); + return; + } + await this.poolSelector.click(); - const item = this.page.locator(`[data-qa="pool-selector-row-${pool}"]`); - await item; + const item = this.locator(`pool-selector-row-${pool}`, this.page.locator("body")); + await item.waitForVisible(); await item.click(); await this.page.waitForSelector(`[data-qa="pool-selector-row-${pool}"]`, { @@ -228,14 +235,14 @@ class EditModal extends Modal { confirmButton = this.locator("confirm-button"); tabs = new PositionEditTabs(this.page, this.wallet, this.root); + highPriceImpact = this.locator("high-price-impact-warning"); + highSwapImpact = this.locator("high-swap-impact-warning"); + async deposit(amount: string) { await this.tabs.select("Deposit"); await this.input.locator("input").fill(amount); - expect(this.confirmButton).toBeEnabled(); - - await this.confirmButton.click(); - await this.wallet.confirmTransaction(); + await this.confirm(); } async withdraw(amount: string) { @@ -248,14 +255,25 @@ class EditModal extends Modal { await this.input.fill(amount); } - expect(this.confirmButton).toBeEnabled(); - - await this.confirmButton.click(); - await this.wallet.confirmTransaction(); + await this.confirm(); } - async confirm() { + async confirm(agree = true) { + if (agree) { + const hasPriceImpactWarning = await this.has(this.highPriceImpact); + const hasSwapImpactWarning = await this.has(this.highSwapImpact); + + if (hasPriceImpactWarning) { + await this.highPriceImpact.click(); + } + + if (hasSwapImpactWarning) { + await this.highSwapImpact.click(); + } + } + await this.confirmButton.click(); + await this.wallet.confirmTransaction(); } } @@ -264,6 +282,9 @@ class CloseModal extends Modal { confirmButton = this.locator("confirm-button"); tabs = new PositionCloseTabs(this.page, this.wallet, this.root); + highPriceImpact = this.locator("high-price-impact-warning"); + highSwapImpact = this.locator("high-swap-impact-warning"); + async closePartially(amount: string) { await this.tabs.select("Market"); @@ -274,26 +295,34 @@ class CloseModal extends Modal { await this.input.fill(amount); } - expect(this.confirmButton).toBeEnabled(); - - await this.confirmButton.click(); - await this.wallet.confirmTransaction(); + await this.confirm(); } async closeFull() { await this.tabs.select("Market"); await this.input.locator("input").click(); - await this.input.locator(`[data-qa="amount-input-max"]`).click(); + await this.input.locator(`[data-qa="input-max"]`).click(); - expect(this.confirmButton).toBeEnabled(); - - await this.confirmButton.click(); - await this.wallet.confirmTransaction(); + await this.confirm(); } - async confirm() { + async confirm(agree = true) { + if (agree) { + const hasPriceImpactWarning = await this.has(this.highPriceImpact); + const hasSwapImpactWarning = await this.has(this.highSwapImpact); + + if (hasPriceImpactWarning) { + await this.highPriceImpact.click(); + } + + if (hasSwapImpactWarning) { + await this.highSwapImpact.click(); + } + } + await this.confirmButton.click(); + await this.wallet.confirmTransaction(); } } @@ -304,6 +333,13 @@ class Position extends BasePage { editModal?: EditModal; closeModal?: CloseModal; + async waitForLoadingEnd() { + const loading = this.locator("position-loading"); + + await loading.waitForVisible(); + await loading.waitForDetached(); + } + async getCollateral(): Promise { return await this.locator("position-collateral-value").evaluate((el) => el.textContent); } @@ -332,6 +368,9 @@ class Position extends BasePage { state: "detached", }); this.closeModal = undefined; + + await this.waitForTransactionToBeSent(); + await this.waitForLoadingEnd(); } async closePartially(amount: string) { @@ -346,6 +385,9 @@ class Position extends BasePage { state: "detached", }); this.closeModal = undefined; + + await this.waitForTransactionToBeSent(); + await this.waitForLoadingEnd(); } async deposit(amount: string) { @@ -361,7 +403,8 @@ class Position extends BasePage { }); this.editModal = undefined; - this.waitForTransactionToBeSent(); + await this.waitForTransactionToBeSent(); + await this.waitForLoadingEnd(); } async withdraw(amount: string) { @@ -376,6 +419,9 @@ class Position extends BasePage { state: "detached", }); this.editModal = undefined; + + await this.waitForTransactionToBeSent(); + await this.waitForLoadingEnd(); } } @@ -401,6 +447,7 @@ export class GmxApp extends BasePage { constructor(page: Page, wallet: Dappwright, baseUrl: string) { super(page, wallet); this.baseUrl = baseUrl; + this.root = this.page.locator("body"); } async closeAllToasts() { @@ -418,15 +465,11 @@ export class GmxApp extends BasePage { } async getPosition(market: string, pool: string, direction: "Long" | "Short") { - const position = new Position( + return new Position( this.page, this.wallet, this.locator(`[data-qa="trade-table-large"] [data-qa="position-item-${market}-${pool}-${direction}"]`) ); - - await position.root.waitForVisible(); - - return position; } async navigateTo(page: GmxNavigation) { diff --git a/autotests/src/tests/trade.spec.ts b/autotests/src/tests/trade.spec.ts index daa90a385a..37c358a89e 100644 --- a/autotests/src/tests/trade.spec.ts +++ b/autotests/src/tests/trade.spec.ts @@ -1,8 +1,11 @@ import { expect } from "@playwright/test"; import { test } from "../base"; -import get from "lodash/get"; test.describe.serial("Trades", () => { + test.afterEach(async ({ page }) => { + await page.close(); + }); + test.describe("Market", () => { test("increase market position", async ({ page, gmx }) => { await page.goto(gmx.baseUrl); @@ -10,7 +13,7 @@ test.describe.serial("Trades", () => { await gmx.tradebox.selectDirection("Long"); await gmx.tradebox.selectMode("Market"); - await gmx.tradebox.selectMarket("BNB/USD"); + await gmx.tradebox.selectMarket("WBTC/USD"); await gmx.tradebox.selectCollateral("AVAX"); await gmx.tradebox.selectPool("WBTC-USDC"); @@ -23,22 +26,24 @@ test.describe.serial("Trades", () => { await gmx.tradebox.confirmTradeButton.click(); await gmx.wallet.confirmTransaction(); + await gmx.page.waitForTimeout(1000); const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); + const isPositionPresent = await gmx.has(position.root); - await position.root.waitForSelector(); - expect(position.root).toBeAttached(); + expect(isPositionPresent).toBeTruthy(); }); test("edit position deposit", async ({ page, gmx }) => { await page.goto(gmx.baseUrl); const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); + position.root.waitForVisible(); expect(position.root).toBeVisible(); const collateral = await position.getCollateral(); - await position.deposit("0.2"); + await position.deposit("2"); const newCollateral = await position.getCollateral(); expect(newCollateral !== collateral).toBeTruthy(); @@ -48,6 +53,7 @@ test.describe.serial("Trades", () => { await page.goto(gmx.baseUrl); const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); + position.root.waitForVisible(); expect(position.root).toBeVisible(); const collateral = await position.getCollateral(); @@ -62,34 +68,39 @@ test.describe.serial("Trades", () => { await page.goto(gmx.baseUrl); const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); + position.root.waitForVisible(); expect(position.root).toBeVisible(); + const collateral = await position.getCollateral(); + await position.closePartially("25%"); + + const newCollateral = await position.getCollateral(); + expect(newCollateral !== collateral).toBeTruthy(); }); test("close position full", async ({ page, gmx }) => { await page.goto(gmx.baseUrl); const position = await gmx.getPosition("WBTC/USD", "WBTC-USDC", "Long"); + position.root.waitForVisible(); expect(position.root).toBeVisible(); await position.closeFull(); - await position.root.waitFor({ - state: "detached", - }); + await position.root.waitForDetached(); expect(position.root).not.toBeAttached(); }); }); - test.describe("Limit", () => { + test.describe.skip("Limit", () => { test("create limit position", async ({ page, gmx }) => { await page.goto(gmx.baseUrl); await gmx.tradebox.selectDirection("Long"); await gmx.tradebox.selectMode("Limit"); - await gmx.tradebox.selectMarket("BNB/USD"); + await gmx.tradebox.selectMarket("BTC/USD"); await gmx.tradebox.selectCollateral("AVAX"); await gmx.tradebox.selectPool("WBTC-USDC"); diff --git a/src/components/BuyInputSection/BuyInputSection.tsx b/src/components/BuyInputSection/BuyInputSection.tsx index 089dec1b73..88768f4007 100644 --- a/src/components/BuyInputSection/BuyInputSection.tsx +++ b/src/components/BuyInputSection/BuyInputSection.tsx @@ -118,7 +118,7 @@ export default function BuyInputSection(props: Props) { )} {staticInput &&
{inputValue}
} {showMaxButton && ( - )} diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index bab5d35df3..68eac98d9b 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -15,6 +15,7 @@ type Props = { children?: ReactNode; asRow?: boolean; isPartialChecked?: boolean; + qa?: string; }; export default function Checkbox(props: Props) { @@ -27,6 +28,7 @@ export default function Checkbox(props: Props) { setIsChecked?.(!isChecked); event.stopPropagation(); }} + data-qa={props.qa} > {isPartialChecked && } diff --git a/src/components/Exchange/ExchangeInfoRow.tsx b/src/components/Exchange/ExchangeInfoRow.tsx index f00c3de6fa..670dee400c 100644 --- a/src/components/Exchange/ExchangeInfoRow.tsx +++ b/src/components/Exchange/ExchangeInfoRow.tsx @@ -18,7 +18,7 @@ export default function ExchangeInfoRow(props: Props) {
{label}
{children || value}
diff --git a/src/components/Synthetics/HighPriceImpactWarning/HighPriceImpactWarning.tsx b/src/components/Synthetics/HighPriceImpactWarning/HighPriceImpactWarning.tsx index 31dada9ce2..dbf2c7b55c 100644 --- a/src/components/Synthetics/HighPriceImpactWarning/HighPriceImpactWarning.tsx +++ b/src/components/Synthetics/HighPriceImpactWarning/HighPriceImpactWarning.tsx @@ -22,6 +22,7 @@ export function HighPriceImpactWarning({ priceImpactWarningState, className }: P asRow isChecked={priceImpactWarningState.isHighPositionImpactAccepted} setIsChecked={priceImpactWarningState.setIsHighPositionImpactAccepted} + qa="high-price-impact-warning" > Acknowledge high Price Impact @@ -34,6 +35,7 @@ export function HighPriceImpactWarning({ priceImpactWarningState, className }: P asRow isChecked={priceImpactWarningState.isHighSwapImpactAccepted} setIsChecked={priceImpactWarningState.setIsHighSwapImpactAccepted} + qa="high-swap-impact-warning" > Acknowledge high Swap Price Impact diff --git a/src/components/Synthetics/PositionItem/PositionItem.tsx b/src/components/Synthetics/PositionItem/PositionItem.tsx index e54d9ddd52..81641858c1 100644 --- a/src/components/Synthetics/PositionItem/PositionItem.tsx +++ b/src/components/Synthetics/PositionItem/PositionItem.tsx @@ -403,7 +403,9 @@ export function PositionItem(p: Props) {
)} /> - {p.position.pendingUpdate && } + {p.position.pendingUpdate && ( + + )}
{formatLeverage(p.position.leverage) || "..."}  diff --git a/src/components/Synthetics/PositionSeller/PositionSeller.tsx b/src/components/Synthetics/PositionSeller/PositionSeller.tsx index 4038a8ba1b..22bcd4e55b 100644 --- a/src/components/Synthetics/PositionSeller/PositionSeller.tsx +++ b/src/components/Synthetics/PositionSeller/PositionSeller.tsx @@ -511,12 +511,14 @@ export function PositionSeller(p: Props) { Close {position?.isLong ? t`Long` : t`Short`} {position?.indexToken?.symbol} } + qa="position-close-modal" > {position && ( @@ -535,6 +537,7 @@ export function PositionSeller(p: Props) { const formattedAmount = formatAmountFree((maxCloseSize * BigInt(percentage)) / 100n, USD_DECIMALS, 2); setCloseUsdInputValueRaw(formattedAmount); }} + qa="amount-input" > USD @@ -553,6 +556,7 @@ export function PositionSeller(p: Props) { onInputValueChange={(e) => { setTriggerPriceInputValue(e.target.value); }} + qa="trigger-input" > USD @@ -595,6 +599,7 @@ export function PositionSeller(p: Props) { disabled={Boolean(error) && !shouldDisableValidationForTesting} onClick={onSubmit} buttonRef={submitButtonRef} + qa="confirm-button" > {error || (isTrigger diff --git a/src/components/Synthetics/SelectorBase/SelectorBase.tsx b/src/components/Synthetics/SelectorBase/SelectorBase.tsx index 283b49ad90..4d337cdb37 100644 --- a/src/components/Synthetics/SelectorBase/SelectorBase.tsx +++ b/src/components/Synthetics/SelectorBase/SelectorBase.tsx @@ -157,7 +157,14 @@ function SelectorBaseDesktop(props: Props & { qa?: string }) { }, []); if (props.disabled) { - return
{props.label}
; + return ( +
+ {props.label} +
+ ); } return ( From 361a8511ca926979f635457a867c59403d22a363 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Wed, 7 Aug 2024 16:24:26 +0000 Subject: [PATCH 07/62] Dynamic rebates --- .../Synthetics/TradeFeesRow/TradeFeesRow.tsx | 22 +++++++------------ .../synthetics/common/useIncentiveStats.ts | 13 ++++++----- .../tokens/useOracleKeeperFetcher.ts | 8 +++++-- .../__tests__/subscribeBars.spec.ts | 5 ++++- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/components/Synthetics/TradeFeesRow/TradeFeesRow.tsx b/src/components/Synthetics/TradeFeesRow/TradeFeesRow.tsx index a608bd2171..220c64c023 100644 --- a/src/components/Synthetics/TradeFeesRow/TradeFeesRow.tsx +++ b/src/components/Synthetics/TradeFeesRow/TradeFeesRow.tsx @@ -3,10 +3,13 @@ import cx from "classnames"; import { ReactNode, useMemo } from "react"; import { BASIS_POINTS_DIVISOR_BIGINT } from "config/factors"; +import { getIncentivesV2Url } from "config/links"; import { getToken } from "config/tokens"; import { useTradingIncentives } from "domain/synthetics/common/useIncentiveStats"; import { FeeItem, SwapFeeItem } from "domain/synthetics/fees"; +import { useTradingAirdroppedTokenTitle } from "domain/synthetics/tokens/useAirdroppedTokenTitle"; import { TradeFeesType } from "domain/synthetics/trade"; +import { bigMath } from "lib/bigmath"; import { useChainId } from "lib/chains"; import { formatAmount, formatDeltaUsd, formatPercentage } from "lib/numbers"; import { getPositiveOrNegativeClass } from "lib/utils"; @@ -16,15 +19,9 @@ import ExternalLink from "components/ExternalLink/ExternalLink"; import StatsTooltipRow from "components/StatsTooltip/StatsTooltipRow"; import TooltipWithPortal from "components/Tooltip/TooltipWithPortal"; -import { ARBITRUM } from "config/chains"; -import { getIncentivesV2Url } from "config/links"; -import { useTradingAirdroppedTokenTitle } from "domain/synthetics/tokens/useAirdroppedTokenTitle"; import sparkleIcon from "img/sparkle.svg"; -import { bigMath } from "lib/bigmath"; -import "./TradeFeesRow.scss"; -const HARDCODED_ESTIMATED_REBATE_PERCENT_ARB = 2500n; -const HARDCODED_ESTIMATED_REBATE_PERCENT_AVAX = 7500n; +import "./TradeFeesRow.scss"; type Props = { totalFees?: FeeItem; @@ -59,10 +56,7 @@ export function TradeFeesRow(p: Props) { const incentivesTokenTitle = useTradingAirdroppedTokenTitle(); const shouldShowRebate = p.shouldShowRebate ?? true; - const isArbitrum = chainId === ARBITRUM; - const estimatedRebatesPercentage = isArbitrum - ? HARDCODED_ESTIMATED_REBATE_PERCENT_ARB - : HARDCODED_ESTIMATED_REBATE_PERCENT_AVAX; + const estimatedRebatesPercentage = tradingIncentives?.estimatedRebatePercent ?? 0n; const rebateIsApplicable = shouldShowRebate && p.positionFee?.deltaUsd !== undefined && p.positionFee.deltaUsd <= 0 && p.feesType !== "swap"; @@ -331,8 +325,8 @@ export function TradeFeesRow(p: Props) { return ( - The bonus rebate is an estimate and can be up to {formatAmount(tradingIncentives?.rebatePercent, 2, 0)}% of the - open fee. It will be airdropped as {incentivesTokenTitle} tokens on a pro-rata basis.{" "} + The bonus rebate is an estimate and can be up to {formatAmount(tradingIncentives?.maxRebatePercent, 2, 0)}% of + the open fee. It will be airdropped as {incentivesTokenTitle} tokens on a pro-rata basis.{" "} Read more @@ -341,7 +335,7 @@ export function TradeFeesRow(p: Props) { ); - }, [chainId, incentivesTokenTitle, rebateIsApplicable, tradingIncentives?.rebatePercent]); + }, [chainId, incentivesTokenTitle, rebateIsApplicable, tradingIncentives?.maxRebatePercent]); const swapRouteMsg = useMemo(() => { if (p.swapFees && p.swapFees.length <= 2) return; diff --git a/src/domain/synthetics/common/useIncentiveStats.ts b/src/domain/synthetics/common/useIncentiveStats.ts index 7e79d890e5..21a4bd442f 100644 --- a/src/domain/synthetics/common/useIncentiveStats.ts +++ b/src/domain/synthetics/common/useIncentiveStats.ts @@ -60,20 +60,21 @@ export const useTradingIncentives = (chainId: number) => { return null; } - const rebatePercent = BigInt(raw.rebatePercent); - if (rebatePercent == 0n) return null; + const maxRebatePercent = BigInt(raw.maxRebatePercent); + if (maxRebatePercent === 0n) return null; + + const estimatedRebatePercent = BigInt(raw.estimatedRebatePercent); const allocation = BigInt(raw.allocation); const nextPeriodStart = addDays(new Date(startOfPeriod * 1000), 7); - const tokenAddress = raw.token ?? (incentiveStats?.lp.isActive ? incentiveStats.lp.token : null); - return { allocation, period: raw.period, nextPeriodStart, - rebatePercent, - token: tokenAddress, + maxRebatePercent, + estimatedRebatePercent, + token: raw.token, }; }, [incentiveStats, startOfPeriod]); }; diff --git a/src/domain/synthetics/tokens/useOracleKeeperFetcher.ts b/src/domain/synthetics/tokens/useOracleKeeperFetcher.ts index 736a27df2e..e6d0da137d 100644 --- a/src/domain/synthetics/tokens/useOracleKeeperFetcher.ts +++ b/src/domain/synthetics/tokens/useOracleKeeperFetcher.ts @@ -47,11 +47,15 @@ export type RawIncentivesStats = { period: number; }>; trading: OnlyWhenActive<{ + /** + * @deprecated use `maxRebatePercent` or `estimatedRebatePercent` instead + */ rebatePercent: number; + maxRebatePercent: number; + estimatedRebatePercent: number; allocation: string; period: number; - // not yet implemented on keeper side - token?: string; + token: Address; }>; }; diff --git a/src/domain/tradingview/__tests__/subscribeBars.spec.ts b/src/domain/tradingview/__tests__/subscribeBars.spec.ts index 8b5c580cf4..468e790030 100644 --- a/src/domain/tradingview/__tests__/subscribeBars.spec.ts +++ b/src/domain/tradingview/__tests__/subscribeBars.spec.ts @@ -64,10 +64,13 @@ class MockOracleKeeperFetcher implements OracleFetcher { trading: { isActive: true, rebatePercent: 0, + estimatedRebatePercent: 0, + maxRebatePercent: 0, + token: "0x912CE59144191C1204E64559FE8253a0e49E6548", allocation: "0", period: 0, }, - }); + } satisfies RawIncentivesStats); } fetchPostReport(): Promise { From a20bf85f5dcf76c6a9ab9e9e169fae9d0911d16e Mon Sep 17 00:00:00 2001 From: Divhead Date: Thu, 8 Aug 2024 15:40:56 +0300 Subject: [PATCH 08/62] ui metrics --- src/App/App.tsx | 21 +- src/App/AppRoutes.tsx | 2 +- .../PositionEditor/PositionEditor.tsx | 58 ++++ .../Synthetics/PositionList/PositionList.tsx | 21 +- .../PositionSeller/PositionSeller.tsx | 68 ++++- .../hooks/useTradeboxTransactions.tsx | 253 +++++++++++++++--- src/context/MetricsContext/MetricsContext.tsx | 131 +++++++++ .../MetricsContext}/errorReporting.ts | 106 +++++--- src/context/MetricsContext/utils.ts | 45 ++++ .../SyntheticsEventsProvider.tsx | 51 ++++ src/context/SyntheticsEvents/types.ts | 1 + src/domain/legacy.ts | 1 + .../orders/createDecreaseOrderTxn.ts | 4 +- .../orders/createIncreaseOrderTxn.ts | 3 + .../synthetics/orders/createSwapOrderTxn.ts | 16 +- .../tokens/useOracleKeeperFetcher.ts | 11 + .../__tests__/subscribeBars.spec.ts | 10 +- src/lib/contracts/callContract.tsx | 2 + src/lib/contracts/transactionErrors.tsx | 4 + src/lib/version.ts | 5 + src/lib/wallets/getWalletNames.ts | 25 ++ src/pages/SyntheticsPage/SyntheticsPage.tsx | 142 +++++----- 22 files changed, 829 insertions(+), 151 deletions(-) create mode 100644 src/context/MetricsContext/MetricsContext.tsx rename src/{lib => context/MetricsContext}/errorReporting.ts (68%) create mode 100644 src/context/MetricsContext/utils.ts create mode 100644 src/lib/version.ts create mode 100644 src/lib/wallets/getWalletNames.ts diff --git a/src/App/App.tsx b/src/App/App.tsx index 13f349900f..9175b1dcd0 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -36,6 +36,7 @@ import { useEthersSigner } from "lib/wallets/useEthersSigner"; import ExternalLink from "components/ExternalLink/ExternalLink"; import { AppRoutes } from "./AppRoutes"; +import { MetricsContextProvider, useMetrics } from "context/MetricsContext/MetricsContext"; // @ts-ignore if (window?.ethereum?.autoRefreshOnNetworkChange) { @@ -53,6 +54,7 @@ const SWRConfigProp = { function App() { const signer = useEthersSigner(); const { chainId } = useChainId(); + const metrics = useMetrics(); const [pendingTxns, setPendingTxns] = useState([]); @@ -73,6 +75,20 @@ function App() {
); + + if (pendingTxn.metricId) { + const metricData = metrics.getPendingEvent(pendingTxn.metricId, true); + const metricType = metricData?.metricType || "unknownTxn"; + + metrics?.sendMetric({ + event: `${metricType}.failed`, + isError: true, + fields: { + metricId: pendingTxn.metricId, + ...(metricData || {}), + }, + }); + } } if (receipt.status === 1 && pendingTxn.message) { @@ -103,7 +119,7 @@ function App() { checkPendingTxns(); }, 2 * 1000); return () => clearInterval(interval); - }, [signer, pendingTxns, chainId]); + }, [signer, pendingTxns, chainId, metrics]); useScrollToTop(); @@ -115,8 +131,9 @@ function App() { let app = ; app = {app}; app = {app}; - app = {app}; app = {app}; + app = {app}; + app = {app}; app = {app}; app = {app}; app = {app}; diff --git a/src/App/AppRoutes.tsx b/src/App/AppRoutes.tsx index 6022471447..cdbfabc528 100644 --- a/src/App/AppRoutes.tsx +++ b/src/App/AppRoutes.tsx @@ -13,7 +13,6 @@ import { TOAST_AUTO_CLOSE_TIME } from "config/ui"; import { decodeReferralCode, encodeReferralCode } from "domain/referrals"; import { useChainId } from "lib/chains"; import { useRealChainIdWarning } from "lib/chains/useRealChainIdWarning"; -import { useErrorReporting } from "lib/errorReporting"; import { REFERRAL_CODE_QUERY_PARAM, getAppBaseUrl, isHomeSite } from "lib/legacy"; import useRouteQuery from "lib/useRouteQuery"; @@ -27,6 +26,7 @@ import { SubaccountModal } from "components/Synthetics/SubaccountModal/Subaccoun import { HomeRoutes } from "./HomeRoutes"; import { MainRoutes } from "./MainRoutes"; +import { useErrorReporting } from "context/MetricsContext/errorReporting"; const Zoom = cssTransition({ enter: "zoomIn", diff --git a/src/components/Synthetics/PositionEditor/PositionEditor.tsx b/src/components/Synthetics/PositionEditor/PositionEditor.tsx index dcf402900d..d8e1cd4346 100644 --- a/src/components/Synthetics/PositionEditor/PositionEditor.tsx +++ b/src/components/Synthetics/PositionEditor/PositionEditor.tsx @@ -79,6 +79,10 @@ import { usePositionEditorFees } from "./hooks/usePositionEditorFees"; import "./PositionEditor.scss"; import { useSelector } from "context/SyntheticsStateContext/utils"; import { makeSelectMarketPriceDecimals } from "context/SyntheticsStateContext/selectors/statsSelectors"; +import { useMetrics } from "context/MetricsContext/MetricsContext"; +import { getPositionOrderMetricId } from "context/MetricsContext/utils"; +import { helperToast } from "lib/helperToast"; +import { isUserRejectedActionError } from "lib/contracts/transactionErrors"; export type Props = { allowedSlippage: number; @@ -111,6 +115,7 @@ export function PositionEditor(p: Props) { const { data: hasOutdatedUi } = useHasOutdatedUi(); const position = usePositionEditorPosition(); const localizedOperationLabels = useLocalizedMap(OPERATION_LABELS); + const metrics = useMetrics(); const submitButtonRef = useRef(null); @@ -320,6 +325,11 @@ export function PositionEditor(p: Props) { return; } + metrics.sendMetric({ + event: "editCollateral.submitted", + isError: false, + }); + if ( executionFee?.feeTokenAmount === undefined || !tokensData || @@ -329,9 +339,34 @@ export function PositionEditor(p: Props) { !selectedCollateralAddress || !signer ) { + helperToast.error(t`Error submitting order`); + metrics.sendMetric({ + event: "editCollateral.fail", + isError: true, + message: "Error submitting order, missed data", + }); return; } + const orderType = isDeposit ? OrderType.MarketIncrease : OrderType.MarketDecrease; + + const metricData = { + metricType: "editCollateral", + marketAddress: position?.marketInfo?.marketTokenAddress, + initialCollateralTokenAddress: selectedCollateralAddress, + initialCollateralDeltaAmount: collateralDeltaAmount, + targetCollateralAddress: position?.collateralTokenAddress, + collateralDeltaAmount, + sizeDeltaUsd: 0n, + swapPath: [], + isLong: position?.isLong, + orderType, + executionFee: executionFee?.feeTokenAmount, + }; + + const metricId = getPositionOrderMetricId(metricData); + metrics.setPendingEvent(metricId, metricData); + let txnPromise: Promise; if (isDeposit) { @@ -341,6 +376,7 @@ export function PositionEditor(p: Props) { chainId, signer, subaccount, + metricId, createIncreaseOrderParams: { account, marketAddress: position.marketAddress, @@ -413,6 +449,28 @@ export function PositionEditor(p: Props) { return; } + txnPromise = txnPromise + .then(() => { + metrics.sendMetric({ + event: "editCollateral.sent", + isError: false, + fields: metrics.getPendingEvent(metricId), + }); + metrics.startTimer(metricId); + + return Promise.resolve(); + }) + .catch((error) => { + metrics.sendMetric({ + event: `editCollateral.${isUserRejectedActionError(error) ? "rejected" : "fail"}`, + isError: true, + message: error.message, + fields: metrics.getPendingEvent(metricId, true), + }); + + throw error; + }); + txnPromise.then(onClose).finally(() => { setIsSubmitting(false); }); diff --git a/src/components/Synthetics/PositionList/PositionList.tsx b/src/components/Synthetics/PositionList/PositionList.tsx index 6837c6cc6c..335b809e51 100644 --- a/src/components/Synthetics/PositionList/PositionList.tsx +++ b/src/components/Synthetics/PositionList/PositionList.tsx @@ -1,5 +1,5 @@ import { Trans, t } from "@lingui/macro"; -import { memo, useCallback, useState } from "react"; +import { memo, useCallback, useEffect, useState } from "react"; import { useIsPositionsLoading, usePositionsInfoData } from "context/SyntheticsStateContext/hooks/globalsHooks"; import { usePositionEditorPositionState } from "context/SyntheticsStateContext/hooks/positionEditorHooks"; @@ -14,6 +14,7 @@ import { getByKey } from "lib/objects"; import PositionShare from "components/Exchange/PositionShare"; import { OrderEditorContainer } from "components/OrderEditorContainer/OrderEditorContainer"; import { PositionItem } from "components/Synthetics/PositionItem/PositionItem"; +import { useMetrics } from "context/MetricsContext/MetricsContext"; type Props = { onSelectPositionClick: (key: string, tradeMode?: TradeMode) => void; @@ -26,9 +27,11 @@ type Props = { export function PositionList(p: Props) { const { onClosePositionClick, onOrdersClick, onSelectPositionClick, openSettings, onCancelOrder, hideActions } = p; + const [isLoaded, setIsLoaded] = useState(false); const positionsInfoData = usePositionsInfoData(); const chainId = useSelector(selectChainId); const account = useSelector(selectAccount); + const metrics = useMetrics(); const [isPositionShareModalOpen, setIsPositionShareModalOpen] = useState(false); const [positionToShareKey, setPositionToShareKey] = useState(); const positionToShare = getByKey(positionsInfoData, positionToShareKey); @@ -40,6 +43,22 @@ export function PositionList(p: Props) { const [, setEditingPositionKey] = usePositionEditorPositionState(); const isLoading = useIsPositionsLoading(); + useEffect(() => { + metrics.startTimer("positionsList"); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + if (positionsInfoData && !isLoaded) { + metrics.sendMetric({ + event: "positionsListLoad.success", + isError: false, + time: metrics.getTime("positionsList"), + }); + setIsLoaded(true); + } + }, [isLoaded, metrics, positionsInfoData]); + return (
{positions.length === 0 && ( diff --git a/src/components/Synthetics/PositionSeller/PositionSeller.tsx b/src/components/Synthetics/PositionSeller/PositionSeller.tsx index 638bd14c57..8f09d5b578 100644 --- a/src/components/Synthetics/PositionSeller/PositionSeller.tsx +++ b/src/components/Synthetics/PositionSeller/PositionSeller.tsx @@ -68,6 +68,10 @@ import { makeSelectMarketPriceDecimals } from "context/SyntheticsStateContext/se import "./PositionSeller.scss"; import { TradeFeesRow } from "../TradeFeesRow/TradeFeesRow"; import { NetworkFeeRow } from "../NetworkFeeRow/NetworkFeeRow"; +import { useMetrics } from "context/MetricsContext/MetricsContext"; +import { getPositionOrderMetricId } from "context/MetricsContext/utils"; +import { helperToast } from "lib/helperToast"; +import { isUserRejectedActionError } from "lib/contracts/transactionErrors"; export type Props = { setPendingTxns: (txns: any) => void; @@ -87,6 +91,7 @@ export function PositionSeller(p: Props) { }, [setClosingPositionKey]); const availableTokensOptions = useSelector(selectTradeboxAvailableTokensOptions); const tokensData = useTokensData(); + const metrics = useMetrics(); const { chainId } = useChainId(); const { signer, account } = useWallet(); const { openConnectModal } = useConnectModal(); @@ -273,6 +278,19 @@ export function PositionSeller(p: Props) { } const orderType = isTrigger ? decreaseAmounts?.triggerOrderType : OrderType.MarketDecrease; + let metricType: string; + if (isTrigger) { + metricType = "triggerOrder"; + } else if (decreaseAmounts?.isFullClose) { + metricType = "closePosition"; + } else { + metricType = "decreasePosition"; + } + + metrics.sendMetric({ + event: `${metricType}.submitted`, + isError: false, + }); if ( !tokensData || @@ -284,9 +302,34 @@ export function PositionSeller(p: Props) { !signer || !orderType ) { + helperToast.error(t`Error submitting order`); + metrics.sendMetric({ + event: `${metricType}.fail`, + isError: true, + message: "Error submitting order, missed data", + }); return; } + const metricData = { + metricType, + place: "positionSeller", + marketAddress: position?.marketInfo?.marketTokenAddress, + initialCollateralTokenAddress: position?.collateralToken?.address, + initialCollateralDeltaAmount: decreaseAmounts?.collateralDeltaAmount, + swapPath: [], + triggerPrice: decreaseAmounts?.triggerPrice, + acceptablePrice: decreaseAmounts?.acceptablePrice, + sizeDeltaUsd: decreaseAmounts?.sizeDeltaUsd, + sizeDeltaInTokens: decreaseAmounts?.sizeDeltaInTokens, + orderType, + isLong: position.isLong, + executionFee: executionFee?.feeTokenAmount, + }; + + const metricId = getPositionOrderMetricId(metricData); + metrics.setPendingEvent(metricId, metricData); + setIsSubmitting(true); // TODO findSwapPath considering decreasePositionSwapType? @@ -325,8 +368,29 @@ export function PositionSeller(p: Props) { setPendingOrder, setPendingTxns, setPendingPosition, - } - ); + }, + metricId + ) + .then(() => { + metrics.sendMetric({ + event: `${metricType}.sent`, + isError: false, + fields: metrics.getPendingEvent(metricId), + }); + metrics.startTimer(metricId); + + return Promise.resolve(); + }) + .catch((error) => { + metrics.sendMetric({ + event: `${metricType}.${isUserRejectedActionError(error) ? "rejected" : "fail"}`, + isError: true, + message: error.message, + fields: metrics.getPendingEvent(metricId, true), + }); + + throw error; + }); if (subaccount) { onClose(); diff --git a/src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx b/src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx index 5fd4f02b9c..3cea549423 100644 --- a/src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx +++ b/src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx @@ -1,4 +1,6 @@ import { t } from "@lingui/macro"; +import { useMetrics } from "context/MetricsContext/MetricsContext"; +import { getPositionOrderMetricId, getSwapOrderMetricId } from "context/MetricsContext/utils"; import { useSettings } from "context/SettingsContext/SettingsContextProvider"; import { useSubaccount } from "context/SubaccountContext/SubaccountContext"; import { useSyntheticsEvents } from "context/SyntheticsEvents"; @@ -13,6 +15,7 @@ import { selectTradeboxFromTokenAddress, selectTradeboxIncreasePositionAmounts, selectTradeboxMarketInfo, + selectTradeboxSelectedPosition, selectTradeboxSwapAmounts, selectTradeboxToTokenAddress, selectTradeboxTradeFlags, @@ -28,6 +31,7 @@ import { } from "domain/synthetics/orders"; import { createWrapOrUnwrapTxn } from "domain/synthetics/orders/createWrapOrUnwrapTxn"; import { useChainId } from "lib/chains"; +import { isUserRejectedActionError } from "lib/contracts/transactionErrors"; import { helperToast } from "lib/helperToast"; import { getByKey } from "lib/objects"; import useWallet from "lib/wallets/useWallet"; @@ -56,7 +60,7 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions const decreaseAmounts = useSelector(selectTradeboxDecreasePositionAmounts); const { shouldDisableValidationForTesting } = useSettings(); - + const selectedPosition = useSelector(selectTradeboxSelectedPosition); const executionFee = useSelector(selectTradeboxExecutionFee); const triggerPrice = useSelector(selectTradeboxTriggerPrice); const fixedTriggerThresholdType = useSelector(selectTradeboxFixedTriggerThresholdType); @@ -74,8 +78,34 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions const subaccount = useSubaccount(summaryExecutionFee?.feeTokenAmount ?? null, requiredActions); + const metrics = useMetrics(); + const onSubmitSwap = useCallback( function onSubmitSwap() { + const orderType = isLimit ? OrderType.LimitSwap : OrderType.MarketSwap; + const metricType = isLimit ? "limitSwap" : "swap"; + + const metricData = { + metricType, + fromTokenAddress: fromToken?.address, + toTokenAddress: toToken?.address, + fromTokenAmount: swapAmounts?.amountIn, + minOutputAmount: swapAmounts?.minOutputAmount, + swapPath: swapAmounts?.swapPathStats?.swapPath, + executionFee: executionFee?.feeTokenAmount, + allowedSlippage, + orderType, + }; + + const metricId = getSwapOrderMetricId(metricData); + metrics.setPendingEvent(metricId, metricData); + + metrics.sendMetric({ + event: `${metricType}.submitted`, + isError: false, + fields: metrics.getPendingEvent(metricId), + }); + if ( !account || !tokensData || @@ -87,6 +117,16 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions typeof allowedSlippage !== "number" ) { helperToast.error(t`Error submitting order`); + metrics.sendMetric({ + event: `${metricType}.fail`, + isError: true, + message: "Error submitting order", + fields: { + isTokensDataLoaded: Boolean(tokensData), + isSignerLoaded: Boolean(signer), + ...metrics.getPendingEvent(metricId, true), + }, + }); return Promise.resolve(); } @@ -96,7 +136,7 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions fromTokenAmount: swapAmounts.amountIn, swapPath: swapAmounts.swapPathStats?.swapPath, toTokenAddress: toToken.address, - orderType: isLimit ? OrderType.LimitSwap : OrderType.MarketSwap, + orderType, minOutputAmount: swapAmounts.minOutputAmount, referralCode: referralCodeForTxn, executionFee: executionFee.feeTokenAmount, @@ -104,28 +144,67 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions tokensData, setPendingTxns, setPendingOrder, - }); + metricId, + }) + .then(() => { + metrics.sendMetric({ + event: `${metricType}.sent`, + isError: false, + fields: metrics.getPendingEvent(metricId), + }); + + metrics.startTimer(metricId); + + return Promise.resolve(); + }) + .catch((error) => { + metrics.sendMetric({ + event: `${metricType}.${isUserRejectedActionError(error) ? "rejected" : "fail"}`, + isError: true, + message: error.message, + fields: metrics.getPendingEvent(metricId, true), + }); + + throw error; + }); }, [ + metrics, + fromToken, + toToken, + swapAmounts, account, + tokensData, + executionFee, + signer, allowedSlippage, chainId, - executionFee, - fromToken, + subaccount, isLimit, referralCodeForTxn, - setPendingOrder, setPendingTxns, - signer, - subaccount, - swapAmounts, - tokensData, - toToken, + setPendingOrder, ] ); const onSubmitIncreaseOrder = useCallback( function onSubmitIncreaseOrder() { + const orderType = isLimit ? OrderType.LimitIncrease : OrderType.MarketIncrease; + let metricType: string; + + if (isLimit) { + metricType = "limitOrder"; + } else if (!selectedPosition) { + metricType = "openPosition"; + } else { + metricType = "increasePosition"; + } + + metrics.sendMetric({ + event: `${metricType}.submitted`, + isError: false, + }); + if ( !tokensData || !account || @@ -138,9 +217,45 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions typeof allowedSlippage !== "number" ) { helperToast.error(t`Error submitting order`); + metrics.sendMetric({ + event: `${metricType}.fail`, + isError: true, + message: "Error submitting order", + }); return Promise.resolve(); } + const metricData = { + metricType, + marketAddress: marketInfo?.marketTokenAddress, + initialCollateralTokenAddress: fromToken?.address, + initialCollateralDeltaAmount: increaseAmounts?.initialCollateralAmount, + targetCollateralAddress: collateralToken?.address, + collateralDeltaAmount: increaseAmounts?.collateralDeltaAmount, + swapPath: increaseAmounts?.swapPathStats?.swapPath || [], + sizeDeltaUsd: increaseAmounts?.sizeDeltaUsd, + sizeDeltaInTokens: increaseAmounts?.sizeDeltaInTokens, + triggerPrice: isLimit ? triggerPrice : 0n, + acceptablePrice: increaseAmounts?.acceptablePrice, + isLong, + orderType, + executionFee: executionFee?.feeTokenAmount, + }; + + const metricId = getPositionOrderMetricId(metricData); + + metrics.setPendingEvent(metricId, metricData); + + // DEBUG EVENTS + // [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(() => { + // metrics.sendMetric({ + // event: "increasePosition.submitted", + // isError: false, + // time: Math.floor(Math.random() * (15000 - 1200 + 1)) + 1200, + // fields: metrics.getPendingEvent(metricId), + // }); + // }); + const commonSecondaryOrderParams = { account, marketAddress: marketInfo.marketTokenAddress, @@ -156,6 +271,7 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions chainId, signer, subaccount, + metricId, createIncreaseOrderParams: { account, marketAddress: marketInfo.marketTokenAddress, @@ -219,37 +335,65 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions txnType: entry.txnType!, initialCollateralDeltaAmount: entry.order?.initialCollateralDeltaAmount ?? 0n, })), - }); + }) + .then(() => { + metrics.sendMetric({ + event: `${metricType}.sent`, + isError: false, + fields: metrics.getPendingEvent(metricId), + }); + + metrics.startTimer(metricId); + + return Promise.resolve(); + }) + .catch((error) => { + metrics.sendMetric({ + event: `${metricType}.${isUserRejectedActionError(error) ? "rejected" : "fail"}`, + isError: true, + message: error.message, + fields: metrics.getPendingEvent(metricId, true), + }); + + throw error; + }); }, [ - account, - allowedSlippage, - collateralToken, - createSltpEntries, - executionFee, - fromToken, - increaseAmounts, isLimit, + selectedPosition, marketInfo, + fromToken, + increaseAmounts, + collateralToken, + triggerPrice, + isLong, + executionFee, + metrics, + tokensData, + account, + signer, + allowedSlippage, + chainId, + subaccount, referralCodeForTxn, + shouldDisableValidationForTesting, + setPendingTxns, setPendingOrder, setPendingPosition, - setPendingTxns, - shouldDisableValidationForTesting, - signer, - subaccount, - tokensData, - triggerPrice, - updateSltpEntries, + createSltpEntries, cancelSltpEntries, - chainId, - isLong, + updateSltpEntries, getExecutionFeeAmountForEntry, ] ); const onSubmitDecreaseOrder = useCallback( function onSubmitDecreaseOrder() { + metrics.sendMetric({ + event: "triggerOrder.submitted", + isError: false, + }); + if ( !account || !marketInfo || @@ -264,9 +408,33 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions typeof allowedSlippage !== "number" ) { helperToast.error(t`Error submitting order`); + metrics.sendMetric({ + event: "triggerOrder.fail", + isError: true, + message: "Error submitting order, missed data", + }); return Promise.resolve(); } + const metricData = { + metricType: "triggerOrder", + place: "tradeBox", + marketAddress: marketInfo?.marketTokenAddress, + initialCollateralTokenAddress: collateralToken?.address, + initialCollateralDeltaAmount: decreaseAmounts.collateralDeltaAmount, + swapPath: [], + triggerPrice: decreaseAmounts?.triggerPrice, + acceptablePrice: decreaseAmounts?.acceptablePrice, + sizeDeltaUsd: decreaseAmounts?.sizeDeltaUsd, + sizeDeltaInTokens: decreaseAmounts?.sizeDeltaInTokens, + orderType: fixedTriggerOrderType, + isLong, + executionFee: executionFee?.feeTokenAmount, + }; + + const metricId = getPositionOrderMetricId(metricData); + metrics.setPendingEvent(metricId, metricData); + return createDecreaseOrderTxn( chainId, signer, @@ -299,8 +467,32 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions setPendingTxns, setPendingOrder, setPendingPosition, - } - ); + }, + metricId + ) + .then(() => { + metrics.sendMetric({ + event: "triggerOrder.sent", + isError: false, + fields: metrics.getPendingEvent(metricId), + }); + + metrics.startTimer(metricId); + + return Promise.resolve(); + }) + .catch((error) => { + if (!isUserRejectedActionError(error)) { + metrics.sendMetric({ + event: "triggerOrder.fail", + isError: true, + message: error.message, + fields: metrics.getPendingEvent(metricId, true), + }); + } + + throw error; + }); }, [ account, @@ -313,6 +505,7 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions fixedTriggerThresholdType, isLong, marketInfo, + metrics, referralCodeForTxn, setPendingOrder, setPendingPosition, diff --git a/src/context/MetricsContext/MetricsContext.tsx b/src/context/MetricsContext/MetricsContext.tsx new file mode 100644 index 0000000000..50343f451d --- /dev/null +++ b/src/context/MetricsContext/MetricsContext.tsx @@ -0,0 +1,131 @@ +import { isDevelopment } from "config/env"; +import { useSubaccountAddress } from "context/SubaccountContext/SubaccountContext"; +import { useOracleKeeperFetcher } from "domain/synthetics/tokens"; +import { useChainId } from "lib/chains"; +import { getAppVersion } from "lib/version"; +import { getWalletNames } from "lib/wallets/getWalletNames"; +import { Context, PropsWithChildren, useMemo, useRef } from "react"; +import { createContext, useContextSelector } from "use-context-selector"; +import mapValues from "lodash/mapValues"; + +export type MetricsContextType = { + sendMetric: (params: { + event: string; + fields?: Record; + time?: number; + isError: boolean; + message?: string; + }) => void; + setPendingEvent: (metricId: string, eventData: T) => T; + getPendingEvent: (metricId: string, clear?: boolean) => any; + startTimer: (metricId: string) => void; + getTime: (metricId: string, clear?: boolean) => number | undefined; +}; + +const context = createContext(null); + +export function MetricsContextProvider({ children }: PropsWithChildren) { + const { chainId } = useChainId(); + const fetcher = useOracleKeeperFetcher(chainId); + const subaccountAddress = useSubaccountAddress(); + + const pendingEvents = useRef({}); + const timers = useRef({}); + + const value: MetricsContextType = useMemo(() => { + const setPendingEvent = (metricId: string, eventData: any) => { + pendingEvents.current[metricId] = { metricId, ...eventData }; + + return eventData; + }; + + const getPendingEvent = (metricId: string, clear?: boolean) => { + const event = pendingEvents.current[metricId]; + + if (clear) { + pendingEvents.current[metricId] = undefined; + } + + return event; + }; + + const startTimer = (metricId: string) => { + timers.current[metricId] = Date.now(); + }; + + const getTime = (metricId: string, clear?: boolean) => { + const time = timers.current[metricId]; + + if (!time) { + return undefined; + } + + if (clear) { + timers.current[metricId] = undefined; + } + + return Date.now() - time; + }; + + async function sendMetric(params: { + event: string; + fields?: any; + time?: number; + isError: boolean; + message?: string; + }) { + const { time, isError, fields, message, event } = params; + const wallets = await getWalletNames(); + + // eslint-disable-next-line no-console + console.log("sendMetric", { + time, + isError, + fields, + message, + event, + }); + + await fetcher.fetchPostReport2({ + is1ct: Boolean(subaccountAddress), + isDev: isDevelopment(), + host: window.location.host, + url: window.location.href, + wallet: wallets.current, + event: event, + version: getAppVersion(), + isError, + time, + customFields: serializeCustomFields({ ...fields, message }), + }); + } + + return { + sendMetric, + setPendingEvent, + getPendingEvent, + startTimer, + getTime, + }; + }, [fetcher, subaccountAddress]); + + return {children}; +} + +export function useMetricsSelector(selector: (s: MetricsContextType) => Selected) { + return useContextSelector(context as Context, selector) as Selected; +} + +export function useMetrics() { + return useMetricsSelector((s) => s); +} + +function serializeCustomFields(fields: Record) { + return mapValues(fields, (v) => { + if (typeof v === "bigint") { + return v.toString(); + } + + return v; + }); +} diff --git a/src/lib/errorReporting.ts b/src/context/MetricsContext/errorReporting.ts similarity index 68% rename from src/lib/errorReporting.ts rename to src/context/MetricsContext/errorReporting.ts index e2087d6fbd..25bbeae341 100644 --- a/src/lib/errorReporting.ts +++ b/src/context/MetricsContext/errorReporting.ts @@ -1,18 +1,86 @@ -import { getAccount } from "@wagmi/core"; import CustomErrors from "abis/CustomErrors.json"; import { isDevelopment, isLocal } from "config/env"; import cryptoJs from "crypto-js"; import { extractDataFromError } from "domain/synthetics/orders/simulateExecuteOrderTxn"; import { OracleFetcher, useOracleKeeperFetcher } from "domain/synthetics/tokens"; import { ethers } from "ethers"; -import { useEffect } from "react"; -import { extractError } from "./contracts/transactionErrors"; -import { rainbowKitConfig } from "./wallets/rainbowKitConfig"; -import { useLocalStorageSerializeKey } from "./localStorage"; +import { useCallback, useEffect, useMemo, useRef } from "react"; +import { extractError } from "../../lib/contracts/transactionErrors"; +import { useLocalStorageSerializeKey } from "../../lib/localStorage"; import { SHOW_DEBUG_VALUES_KEY } from "config/localStorage"; +import { useSubaccountAddress } from "context/SubaccountContext/SubaccountContext"; +import useIsMetamaskMobile from "../../lib/wallets/useIsMetamaskMobile"; +import { useChainId } from "../../lib/chains"; +import { getWalletNames } from "../../lib/wallets/getWalletNames"; +import { getAppVersion } from "../../lib/version"; const IGNORE_ERROR_MESSAGES = ["user rejected action", "failed to fetch"]; +export function useUiMetrics() { + const { chainId } = useChainId(); + const fetcher = useOracleKeeperFetcher(chainId); + const subaccountAddress = useSubaccountAddress(); + const isMetamaskMobile = useIsMetamaskMobile(); + + const pendingEvents = useRef({}); + const timers = useRef({}); + + const setPendingEvent = useCallback((key: string, eventData: any) => { + pendingEvents[key] = eventData; + return eventData; + }, []); + + const getPendingEvent = useCallback((key: string, clear?: boolean) => { + const event = pendingEvents[key]; + + if (clear) { + pendingEvents[key] = undefined; + } + + return event; + }, []); + + const sendMetric = useCallback( + async function sendMetric(params: { + event: string; + fields?: any; + time?: number; + isError: boolean; + message?: string; + }) { + const { time, isError, fields, message, event } = params; + const wallets = await getWalletNames(); + + const body = { + is1ct: Boolean(subaccountAddress), + isDev: isDevelopment(), + host: window.location.host, + url: window.location.href, + wallet: wallets.current, + event: event, + version: getAppVersion(), + isError, + time, + isMetamaskMobile, + message, + customFields: fields, + }; + + fetcher.fetchPostReport2(body); + }, + [fetcher, isMetamaskMobile, subaccountAddress] + ); + + return useMemo( + () => ({ + sendMetric, + setPendingEvent, + getPendingEvent, + }), + [getPendingEvent, sendMetric, setPendingEvent] + ); +} + export function useErrorReporting(chainId: number) { const [showDebugValues] = useLocalStorageSerializeKey(SHOW_DEBUG_VALUES_KEY, false); const fetcher = useOracleKeeperFetcher(chainId); @@ -152,31 +220,3 @@ function hasStack(error: unknown): error is { stack: string } { function hasName(error: unknown): error is { name: string } { return !!error && typeof error === "object" && typeof (error as { name: string }).name === "string"; } - -function getAppVersion() { - return process.env.REACT_APP_VERSION; -} - -async function getWalletNames() { - try { - const walletNames = new Set(); - - for (const connector of rainbowKitConfig.connectors) { - const isAuthorized = await connector.isAuthorized(); - if (isAuthorized) { - walletNames.add(connector.name); - } - } - - return { current: getAccount(rainbowKitConfig).connector?.name, authorized: [...walletNames] }; - } catch (e) { - return { - current: null, - authorized: [], - error: true, - }; - } -} - -(window as any).getWalletNames = getWalletNames; -(window as any).getAppVersion = getAppVersion; diff --git a/src/context/MetricsContext/utils.ts b/src/context/MetricsContext/utils.ts new file mode 100644 index 0000000000..f5602c5e6c --- /dev/null +++ b/src/context/MetricsContext/utils.ts @@ -0,0 +1,45 @@ +import { OrderType } from "domain/synthetics/orders"; + +export function getSwapOrderMetricId(data: { + fromTokenAddress?: string; + toTokenAddress?: string; + fromTokenAmount?: bigint; + minOutputAmount?: bigint; + swapPath?: string[]; + executionFee?: bigint; + orderType?: OrderType; +}) { + return [ + "swap", + data.fromTokenAddress, + data.toTokenAddress, + data.fromTokenAmount?.toString(), + data.minOutputAmount?.toString(), + data.swapPath?.join("-"), + data.executionFee?.toString(), + data.orderType, + ].join(":"); +} + +export function getPositionOrderMetricId(data: { + marketAddress: string; + initialCollateralTokenAddress: string; + initialCollateralDeltaAmount: bigint; + swapPath: string[]; + sizeDeltaUsd: bigint; + isLong: boolean; + orderType: OrderType; + executionFee: bigint; +}) { + return [ + "position", + data.marketAddress, + data.initialCollateralTokenAddress, + data.initialCollateralDeltaAmount?.toString(), + data.swapPath?.join("-"), + data.sizeDeltaUsd?.toString(), + data.isLong, + data.orderType, + data.executionFee?.toString(), + ].join(":"); +} diff --git a/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx b/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx index 51a1cea5db..3e77909be1 100644 --- a/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx +++ b/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -11,6 +11,7 @@ import { isIncreaseOrderType, isLiquidationOrderType, isMarketOrderType, + isSwapOrderType, } from "domain/synthetics/orders"; import { getPositionKey } from "domain/synthetics/positions"; import { useTokensDataRequest } from "domain/synthetics/tokens"; @@ -43,6 +44,8 @@ import { WithdrawalCreatedEventData, WithdrawalStatuses, } from "./types"; +import { useMetrics } from "context/MetricsContext/MetricsContext"; +import { getPositionOrderMetricId, getSwapOrderMetricId } from "context/MetricsContext/utils"; export const SyntheticsEventsContext = createContext({}); @@ -55,6 +58,8 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) const { account: currentAccount } = useWallet(); const { wsProvider } = useWebsocketProvider(); + const metrics = useMetrics(); + const { hasV2LostFocus } = useHasLostFocus(); const { tokensData } = useTokensDataRequest(chainId); @@ -101,6 +106,17 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) return; } + const metricId = isSwapOrderType(data.orderType) ? getSwapOrderMetricId(data) : getPositionOrderMetricId(data); + const metricData = metrics.getPendingEvent(metricId); + const metricType = metricData?.metricType || "unknownOrder"; + + metrics.sendMetric({ + event: `${metricType}.created`, + isError: false, + time: metrics.getTime(metricId), + fields: metricData, + }); + setOrderStatuses((old) => setByKey(old, data.key, { key: data.key, @@ -138,6 +154,24 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) OrderExecuted: (eventData: EventLogData, txnParams: EventTxnParams) => { const key = eventData.bytes32Items.items.key; + const order = orderStatuses[key]?.data; + + if (order) { + const metricId = isSwapOrderType(order.orderType) + ? getSwapOrderMetricId(order) + : getPositionOrderMetricId(order); + + const metricData = metrics.getPendingEvent(metricId, true); + const metricType = metricData?.metricType || "unknownOrder"; + + metrics.sendMetric({ + event: `${metricType}.executed`, + isError: false, + time: metrics.getTime(metricId, true), + fields: metricData, + }); + } + setOrderStatuses((old) => { if (!old[key]) return old; @@ -170,6 +204,23 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) const order = orderStatuses[key]?.data; + if (order) { + const metricId = isSwapOrderType(order.orderType) + ? getSwapOrderMetricId(order) + : getPositionOrderMetricId(order); + + const metricData = metrics.getPendingEvent(metricId, true); + const metricType = metricData?.metricType || "unknownOrder"; + + metrics.sendMetric({ + event: `${metricType}.fail`, + isError: true, + message: `Order cancelled, key: ${order.key}, txn: ${txnParams.transactionHash}`, + time: metrics.getTime(metricId, true), + fields: metricData, + }); + } + // If pending user order is cancelled, reset the pending position state if (order && marketsInfoData) { const wrappedToken = getWrappedToken(chainId); diff --git a/src/context/SyntheticsEvents/types.ts b/src/context/SyntheticsEvents/types.ts index 85b96337bd..174fe93749 100644 --- a/src/context/SyntheticsEvents/types.ts +++ b/src/context/SyntheticsEvents/types.ts @@ -62,6 +62,7 @@ export type PendingOrderData = { isLong: boolean; shouldUnwrapNativeToken: boolean; orderType: OrderType; + referralCode?: string; txnType: OrderTxnType; }; diff --git a/src/domain/legacy.ts b/src/domain/legacy.ts index ea50f42fb1..888913f8db 100644 --- a/src/domain/legacy.ts +++ b/src/domain/legacy.ts @@ -48,6 +48,7 @@ export type PendingTransaction = { hash: string; message: string; messageDetails?: string; + metricId?: string; }; export type SetPendingTransactions = Dispatch>; diff --git a/src/domain/synthetics/orders/createDecreaseOrderTxn.ts b/src/domain/synthetics/orders/createDecreaseOrderTxn.ts index 40644e67f6..0306626e16 100644 --- a/src/domain/synthetics/orders/createDecreaseOrderTxn.ts +++ b/src/domain/synthetics/orders/createDecreaseOrderTxn.ts @@ -53,7 +53,8 @@ export async function createDecreaseOrderTxn( signer: Signer, subaccount: Subaccount, params: DecreaseOrderParams | DecreaseOrderParams[], - callbacks: DecreaseOrderCallbacks + callbacks: DecreaseOrderCallbacks, + metricId?: string ) { const ps = Array.isArray(params) ? params : [params]; const exchangeRouter = new ethers.Contract(getContract(chainId, "ExchangeRouter"), ExchangeRouter.abi, signer); @@ -115,6 +116,7 @@ export async function createDecreaseOrderTxn( hideSentMsg: true, hideSuccessMsg: true, customSigners: subaccount?.customSigners, + metricId, setPendingTxns: callbacks.setPendingTxns, }); diff --git a/src/domain/synthetics/orders/createIncreaseOrderTxn.ts b/src/domain/synthetics/orders/createIncreaseOrderTxn.ts index 304499f76a..13a6e1df58 100644 --- a/src/domain/synthetics/orders/createIncreaseOrderTxn.ts +++ b/src/domain/synthetics/orders/createIncreaseOrderTxn.ts @@ -81,6 +81,7 @@ export async function createIncreaseOrderTxn({ chainId, signer, subaccount, + metricId, createIncreaseOrderParams: p, createDecreaseOrderParams, cancelOrderParams, @@ -89,6 +90,7 @@ export async function createIncreaseOrderTxn({ chainId: number; signer: Signer; subaccount: Subaccount; + metricId?: string; createIncreaseOrderParams: IncreaseOrderParams; createDecreaseOrderParams?: SecondaryDecreaseOrderParams[]; cancelOrderParams?: SecondaryCancelOrderParams[]; @@ -225,6 +227,7 @@ export async function createIncreaseOrderTxn({ hideSentMsg: true, hideSuccessMsg: true, customSigners: subaccount?.customSigners, + metricId, setPendingTxns: p.setPendingTxns, }); diff --git a/src/domain/synthetics/orders/createSwapOrderTxn.ts b/src/domain/synthetics/orders/createSwapOrderTxn.ts index 118dcf2c8a..660b15b6f3 100644 --- a/src/domain/synthetics/orders/createSwapOrderTxn.ts +++ b/src/domain/synthetics/orders/createSwapOrderTxn.ts @@ -1,18 +1,18 @@ +import { t } from "@lingui/macro"; import ExchangeRouter from "abis/ExchangeRouter.json"; import { getContract } from "config/contracts"; import { NATIVE_TOKEN_ADDRESS, convertTokenAddress } from "config/tokens"; -import { SetPendingOrder, PendingOrderData } from "context/SyntheticsEvents"; +import { UI_FEE_RECEIVER_ACCOUNT } from "config/ui"; +import { Subaccount } from "context/SubaccountContext/SubaccountContext"; +import { PendingOrderData, SetPendingOrder } from "context/SyntheticsEvents"; import { Signer, ethers } from "ethers"; import { callContract } from "lib/contracts"; +import { getSubaccountRouterContract } from "../subaccount/getSubaccountContract"; import { TokensData } from "../tokens"; +import { applySlippageToMinOut } from "../trade"; import { simulateExecuteOrderTxn } from "./simulateExecuteOrderTxn"; import { DecreasePositionSwapType, OrderType } from "./types"; -import { applySlippageToMinOut } from "../trade"; import { isMarketOrderType } from "./utils"; -import { UI_FEE_RECEIVER_ACCOUNT } from "config/ui"; -import { t } from "@lingui/macro"; -import { Subaccount } from "context/SubaccountContext/SubaccountContext"; -import { getSubaccountRouterContract } from "../subaccount/getSubaccountContract"; const { ZeroAddress } = ethers; @@ -30,6 +30,7 @@ export type SwapOrderParams = { allowedSlippage: number; setPendingTxns: (txns: any) => void; setPendingOrder: SetPendingOrder; + metricId: string; }; export async function createSwapOrderTxn(chainId: number, signer: Signer, subaccount: Subaccount, p: SwapOrderParams) { @@ -38,7 +39,6 @@ export async function createSwapOrderTxn(chainId: number, signer: Signer, subacc const isNativeReceive = p.toTokenAddress === NATIVE_TOKEN_ADDRESS; subaccount = isNativePayment ? null : subaccount; const router = subaccount ? getSubaccountRouterContract(chainId, subaccount.signer) : exchangeRouter; - const { encodedPayload, totalWntAmount, minOutputAmount } = await getParams(router, signer, subaccount, chainId, p); const { encodedPayload: simulationEncodedPayload, totalWntAmount: sumaltionTotalWntAmount } = await getParams( exchangeRouter, @@ -61,6 +61,7 @@ export async function createSwapOrderTxn(chainId: number, signer: Signer, subacc isLong: false, orderType: p.orderType, shouldUnwrapNativeToken: isNativeReceive, + referralCode: p.referralCode, txnType: "create", }; @@ -85,6 +86,7 @@ export async function createSwapOrderTxn(chainId: number, signer: Signer, subacc hideSuccessMsg: true, customSigners: subaccount?.customSigners, setPendingTxns: p.setPendingTxns, + metricId: p.metricId, }); if (!subaccount) { diff --git a/src/domain/synthetics/tokens/useOracleKeeperFetcher.ts b/src/domain/synthetics/tokens/useOracleKeeperFetcher.ts index 736a27df2e..cc184029bc 100644 --- a/src/domain/synthetics/tokens/useOracleKeeperFetcher.ts +++ b/src/domain/synthetics/tokens/useOracleKeeperFetcher.ts @@ -93,6 +93,7 @@ export interface OracleFetcher { fetchOracleCandles(tokenSymbol: string, period: string, limit: number): Promise>; fetchIncentivesRewards(): Promise; fetchPostReport(body: { report: object; version: string | undefined; isError: boolean }): Promise; + fetchPostReport2(body: any): Promise; } class OracleKeeperFetcher implements OracleFetcher { @@ -195,6 +196,16 @@ class OracleKeeperFetcher implements OracleFetcher { }); } + fetchPostReport2(body: { report: object; version: string | undefined; isError: boolean }): Promise { + return fetch(buildUrl(this.url!, "/report/ui2"), { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + }); + } + async fetchOracleCandles(tokenSymbol: string, period: string, limit: number): Promise> { tokenSymbol = getNormalizedTokenSymbol(tokenSymbol); diff --git a/src/domain/tradingview/__tests__/subscribeBars.spec.ts b/src/domain/tradingview/__tests__/subscribeBars.spec.ts index 8b5c580cf4..35695ef912 100644 --- a/src/domain/tradingview/__tests__/subscribeBars.spec.ts +++ b/src/domain/tradingview/__tests__/subscribeBars.spec.ts @@ -1,11 +1,9 @@ import { ResolutionString } from "charting_library"; import { SUPPORTED_RESOLUTIONS_V2 } from "config/tradingview"; -import { - OracleFetcher, - DayPriceCandle, - RawIncentivesStats, - TickersResponse, -} from "domain/synthetics/tokens/useOracleKeeperFetcher"; +import { TickersResponse } from "context/OracleKeeperContext/oracleKeeperFetcher"; +import { DayPriceCandle } from "context/OracleKeeperContext/oracleKeeperFetcher"; +import { RawIncentivesStats } from "context/OracleKeeperContext/oracleKeeperFetcher"; +import { OracleFetcher } from "context/OracleKeeperContext/oracleKeeperFetcher"; import { SyntheticsTVDataProvider } from "domain/synthetics/tradingview/SyntheticsTVDataProvider"; import { ethers } from "ethers"; import { noop } from "lodash"; diff --git a/src/lib/contracts/callContract.tsx b/src/lib/contracts/callContract.tsx index 26ebef6701..3a9f9fbcb8 100644 --- a/src/lib/contracts/callContract.tsx +++ b/src/lib/contracts/callContract.tsx @@ -26,6 +26,7 @@ export async function callContract( failMsg?: string; customSigners?: Wallet[]; setPendingTxns?: (txns: any) => void; + metricId?: string; } ) { try { @@ -126,6 +127,7 @@ export async function callContract( hash: res.hash, message, messageDetails: opts.detailsMsg, + metricId: opts.metricId, }; opts.setPendingTxns((pendingTxns) => [...pendingTxns, pendingTxn]); } diff --git a/src/lib/contracts/transactionErrors.tsx b/src/lib/contracts/transactionErrors.tsx index c7d81564f7..3a70df1fde 100644 --- a/src/lib/contracts/transactionErrors.tsx +++ b/src/lib/contracts/transactionErrors.tsx @@ -166,6 +166,10 @@ export function getErrorMessage(chainId: number, ex: TxError, txnMessage?: strin return { failMsg, autoCloseToast }; } +export function isUserRejectedActionError(error: Error) { + return error.message.toLowerCase().includes("user rejected action"); +} + export const INVALID_NETWORK_TOAST_ID = "invalid-network"; export function getInvalidNetworkErrorMessage(chainId: number) { return ( diff --git a/src/lib/version.ts b/src/lib/version.ts new file mode 100644 index 0000000000..cb737a4341 --- /dev/null +++ b/src/lib/version.ts @@ -0,0 +1,5 @@ +export function getAppVersion() { + return process.env.REACT_APP_VERSION; +} + +(window as any).getAppVersion = getAppVersion; diff --git a/src/lib/wallets/getWalletNames.ts b/src/lib/wallets/getWalletNames.ts new file mode 100644 index 0000000000..c1d0b8a5ed --- /dev/null +++ b/src/lib/wallets/getWalletNames.ts @@ -0,0 +1,25 @@ +import { getAccount } from "@wagmi/core"; +import { rainbowKitConfig } from "./rainbowKitConfig"; + +export async function getWalletNames() { + try { + const walletNames = new Set(); + + for (const connector of rainbowKitConfig.connectors) { + const isAuthorized = await connector.isAuthorized(); + if (isAuthorized) { + walletNames.add(connector.name); + } + } + + return { current: getAccount(rainbowKitConfig).connector?.name, authorized: [...walletNames] }; + } catch (e) { + return { + current: null, + authorized: [], + error: true, + }; + } +} + +(window as any).getWalletNames = getWalletNames; diff --git a/src/pages/SyntheticsPage/SyntheticsPage.tsx b/src/pages/SyntheticsPage/SyntheticsPage.tsx index cb24c4a131..dfdc3ad08a 100644 --- a/src/pages/SyntheticsPage/SyntheticsPage.tsx +++ b/src/pages/SyntheticsPage/SyntheticsPage.tsx @@ -47,6 +47,7 @@ import { TradeBox } from "components/Synthetics/TradeBox/TradeBox"; import { TradeHistory } from "components/Synthetics/TradeHistory/TradeHistory"; import Tab from "components/Tab/Tab"; import { useInterviewNotification } from "domain/synthetics/userFeedback/useInterviewNotification"; +import { useMedia } from "react-use"; export type Props = { openSettings: () => void; @@ -66,6 +67,8 @@ export function SyntheticsPage(p: Props) { const calcSelector = useCalcSelector(); const [, setPendingTxns] = usePendingTxns(); + const isMobile = useMedia("(max-width: 1100px)"); + const [isSettling, setIsSettling] = useState(false); const [listSection, setListSection] = useLocalStorageSerializeKey( getSyntheticsListSectionKey(chainId), @@ -235,8 +238,77 @@ export function SyntheticsPage(p: Props) {
+ {!isMobile && ( +
+
+ +
+ {listSection === ListSection.Orders && selectedOrderKeys.length > 0 && ( + + )} + + + Chart positions + + +
+
+ + {listSection === ListSection.Positions && ( + + )} + {listSection === ListSection.Orders && ( + + )} + {listSection === ListSection.Trades && } + {listSection === ListSection.Claims && renderClaims()} +
+ )} +
-
+
+
+ +
+
+ + {isMobile && ( +
-
- {listSection === ListSection.Orders && selectedOrderKeys.length > 0 && ( - - )} - - - Chart positions - - -
- {listSection === ListSection.Positions && ( } {listSection === ListSection.Claims && renderClaims()}
-
- -
-
- -
-
- -
-
- -
- {listSection === ListSection.Positions && ( - - )} - {listSection === ListSection.Orders && ( - - )} - {listSection === ListSection.Trades && } - {listSection === ListSection.Claims && renderClaims()} -
+ )}
From 02718ed891d06050e150ceda40596421d37176d1 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Thu, 8 Aug 2024 13:00:45 +0000 Subject: [PATCH 09/62] Fix GM APY sorting --- src/components/Synthetics/GmList/GmList.tsx | 19 ++++++++++++++-- .../Synthetics/GmList/sortGmTokensByField.tsx | 22 +++++++++++++++---- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/components/Synthetics/GmList/GmList.tsx b/src/components/Synthetics/GmList/GmList.tsx index ea3db8c26f..afe5ac9b89 100644 --- a/src/components/Synthetics/GmList/GmList.tsx +++ b/src/components/Synthetics/GmList/GmList.tsx @@ -76,6 +76,7 @@ export function GmList({ marketsTokensApyData, marketsTokensIncentiveAprData, sh orderBy, direction, marketsTokensApyData, + marketsTokensIncentiveAprData, searchText, tokensData, }); @@ -316,6 +317,7 @@ function useFilterSortGmPools({ orderBy, direction, marketsTokensApyData, + marketsTokensIncentiveAprData, searchText, tokensData, }: { @@ -324,9 +326,12 @@ function useFilterSortGmPools({ orderBy: SortField; direction: SortDirection; marketsTokensApyData: MarketTokensAPRData | undefined; + marketsTokensIncentiveAprData: MarketTokensAPRData | undefined; searchText: string; tokensData: TokensData | undefined; }) { + const chainId = useSelector(selectChainId); + const sortedGmTokens = useMemo(() => { if (!marketsInfoData || !marketTokensData) { return []; @@ -337,13 +342,23 @@ function useFilterSortGmPools({ } return sortGmTokensByField({ + chainId, marketsInfoData, marketTokensData, orderBy, direction, - marketsTokensApyData: marketsTokensApyData!, + marketsTokensApyData, + marketsTokensIncentiveAprData, }); - }, [direction, marketTokensData, marketsInfoData, marketsTokensApyData, orderBy]); + }, [ + chainId, + direction, + marketTokensData, + marketsInfoData, + marketsTokensApyData, + marketsTokensIncentiveAprData, + orderBy, + ]); const filteredGmTokens = useMemo(() => { if (!searchText.trim()) { diff --git a/src/components/Synthetics/GmList/sortGmTokensByField.tsx b/src/components/Synthetics/GmList/sortGmTokensByField.tsx index b2d5dd3419..ca64396802 100644 --- a/src/components/Synthetics/GmList/sortGmTokensByField.tsx +++ b/src/components/Synthetics/GmList/sortGmTokensByField.tsx @@ -1,23 +1,29 @@ import { values } from "lodash"; import type { SortDirection } from "components/Sorter/Sorter"; +import { getMarketListingDate } from "config/markets"; import { MarketTokensAPRData, MarketsInfoData, getMintableMarketTokens } from "domain/synthetics/markets"; +import { getIsBaseApyReadyToBeShown } from "domain/synthetics/markets/getIsBaseApyReadyToBeShown"; import { convertToUsd, type TokensData } from "domain/synthetics/tokens"; import type { SortField } from "./GmList"; import { sortGmTokensDefault } from "./sortGmTokensDefault"; export function sortGmTokensByField({ + chainId, marketsInfoData, marketTokensData, orderBy, direction, marketsTokensApyData, + marketsTokensIncentiveAprData, }: { + chainId: number; marketsInfoData: MarketsInfoData; marketTokensData: TokensData; orderBy: SortField; direction: SortDirection; - marketsTokensApyData: MarketTokensAPRData; + marketsTokensApyData: MarketTokensAPRData | undefined; + marketsTokensIncentiveAprData: MarketTokensAPRData | undefined; }) { const gmTokens = values(marketTokensData); const directionMultiplier = direction === "asc" ? 1 : -1; @@ -54,9 +60,17 @@ export function sortGmTokensByField({ if (orderBy === "apy") { return gmTokens.sort((a, b) => { - const aprA = marketsTokensApyData?.[a.address]; - const aprB = marketsTokensApyData?.[b.address]; - return (aprA ?? 0n) > (aprB ?? 0n) ? directionMultiplier : -directionMultiplier; + let aprA = marketsTokensIncentiveAprData?.[a.address] ?? 0n; + if (getIsBaseApyReadyToBeShown(getMarketListingDate(chainId, a.address))) { + aprA += marketsTokensApyData?.[a.address] ?? 0n; + } + + let aprB = marketsTokensIncentiveAprData?.[b.address] ?? 0n; + if (getIsBaseApyReadyToBeShown(getMarketListingDate(chainId, b.address))) { + aprB += marketsTokensApyData?.[b.address] ?? 0n; + } + + return aprA > aprB ? directionMultiplier : -directionMultiplier; }); } From ca0a8dd10064cbdbf355f6c9a938bc0d587a32a1 Mon Sep 17 00:00:00 2001 From: Divhead Date: Thu, 8 Aug 2024 18:46:35 +0300 Subject: [PATCH 10/62] limit custom fields length --- src/context/MetricsContext/MetricsContext.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/context/MetricsContext/MetricsContext.tsx b/src/context/MetricsContext/MetricsContext.tsx index 50343f451d..4753fb8cda 100644 --- a/src/context/MetricsContext/MetricsContext.tsx +++ b/src/context/MetricsContext/MetricsContext.tsx @@ -122,8 +122,14 @@ export function useMetrics() { function serializeCustomFields(fields: Record) { return mapValues(fields, (v) => { + let result; + if (typeof v === "bigint") { - return v.toString(); + result = v.toString(); + } + + if (result.length && result.lenth > 150) { + result = result.slice(0, 150); } return v; From 47408caaef6a62575480e98efeb8955c50ad241c Mon Sep 17 00:00:00 2001 From: Divhead Date: Thu, 8 Aug 2024 18:47:00 +0300 Subject: [PATCH 11/62] limit custom fields length --- src/context/MetricsContext/MetricsContext.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/context/MetricsContext/MetricsContext.tsx b/src/context/MetricsContext/MetricsContext.tsx index 4753fb8cda..60db5312ea 100644 --- a/src/context/MetricsContext/MetricsContext.tsx +++ b/src/context/MetricsContext/MetricsContext.tsx @@ -132,6 +132,6 @@ function serializeCustomFields(fields: Record) { result = result.slice(0, 150); } - return v; + return result; }); } From 09c52b04c1c81fc711ba7c1385043febffdcbe3b Mon Sep 17 00:00:00 2001 From: Divhead Date: Mon, 12 Aug 2024 19:00:52 +0300 Subject: [PATCH 12/62] localStorage cache + review fixes --- src/App/App.tsx | 6 +- .../PositionEditor/PositionEditor.tsx | 89 +++--- .../PositionSeller/PositionSeller.tsx | 102 +++---- .../hooks/useTradeboxTransactions.tsx | 259 ++++++------------ src/config/localStorage.ts | 3 + src/context/MetricsContext/MetricsContext.tsx | 130 ++++++--- src/context/MetricsContext/errorReporting.ts | 74 +---- src/context/MetricsContext/utils.ts | 237 +++++++++++++--- .../SyntheticsEventsProvider.tsx | 48 +++- src/lib/contracts/transactionErrors.tsx | 2 +- src/lib/jsonWithBigint.ts | 25 ++ 11 files changed, 539 insertions(+), 436 deletions(-) create mode 100644 src/lib/jsonWithBigint.ts diff --git a/src/App/App.tsx b/src/App/App.tsx index 9175b1dcd0..d5b4a1e243 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -77,15 +77,17 @@ function App() { ); if (pendingTxn.metricId) { - const metricData = metrics.getPendingEvent(pendingTxn.metricId, true); + const metricData = metrics.getCachedMetricData(pendingTxn.metricId, true); const metricType = metricData?.metricType || "unknownTxn"; metrics?.sendMetric({ event: `${metricType}.failed`, isError: true, + message: "Pending txn error", fields: { - metricId: pendingTxn.metricId, ...(metricData || {}), + metricType, + txnHash: pendingTxn.hash, }, }); } diff --git a/src/components/Synthetics/PositionEditor/PositionEditor.tsx b/src/components/Synthetics/PositionEditor/PositionEditor.tsx index d8e1cd4346..a1594b6b49 100644 --- a/src/components/Synthetics/PositionEditor/PositionEditor.tsx +++ b/src/components/Synthetics/PositionEditor/PositionEditor.tsx @@ -76,13 +76,19 @@ import { PositionEditorAdvancedRows } from "./PositionEditorAdvancedRows"; import { usePositionEditorData } from "./hooks/usePositionEditorData"; import { usePositionEditorFees } from "./hooks/usePositionEditorFees"; -import "./PositionEditor.scss"; -import { useSelector } from "context/SyntheticsStateContext/utils"; -import { makeSelectMarketPriceDecimals } from "context/SyntheticsStateContext/selectors/statsSelectors"; import { useMetrics } from "context/MetricsContext/MetricsContext"; -import { getPositionOrderMetricId } from "context/MetricsContext/utils"; +import { + EditCollateralMetricParams, + getPositionOrderMetricId, + getTxnErrorMetricsHandler, + getTxnSentMetricsHandler, + sendOrderSubmittedMetric, + sendTxnValidationErrorMetric, +} from "context/MetricsContext/utils"; +import { makeSelectMarketPriceDecimals } from "context/SyntheticsStateContext/selectors/statsSelectors"; +import { useSelector } from "context/SyntheticsStateContext/utils"; import { helperToast } from "lib/helperToast"; -import { isUserRejectedActionError } from "lib/contracts/transactionErrors"; +import "./PositionEditor.scss"; export type Props = { allowedSlippage: number; @@ -325,10 +331,25 @@ export function PositionEditor(p: Props) { return; } - metrics.sendMetric({ - event: "editCollateral.submitted", - isError: false, - }); + const orderType = isDeposit ? OrderType.MarketIncrease : OrderType.MarketDecrease; + const metricType = isDeposit ? "depositCollateral" : "withdrawCollateral"; + + const metricData: EditCollateralMetricParams = { + metricType, + account, + marketAddress: position?.marketInfo?.marketTokenAddress, + initialCollateralTokenAddress: selectedCollateralAddress, + initialCollateralDeltaAmount: collateralDeltaAmount, + swapPath: [], + isLong: position?.isLong, + orderType, + executionFee: executionFee?.feeTokenAmount, + }; + + const metricId = getPositionOrderMetricId({ ...metricData, sizeDeltaUsd: 0n }); + metrics.setCachedMetricData(metricId, metricData); + + sendOrderSubmittedMetric(metrics, metricId, metricType); if ( executionFee?.feeTokenAmount === undefined || @@ -340,33 +361,10 @@ export function PositionEditor(p: Props) { !signer ) { helperToast.error(t`Error submitting order`); - metrics.sendMetric({ - event: "editCollateral.fail", - isError: true, - message: "Error submitting order, missed data", - }); + sendTxnValidationErrorMetric(metrics, metricId, metricType); return; } - const orderType = isDeposit ? OrderType.MarketIncrease : OrderType.MarketDecrease; - - const metricData = { - metricType: "editCollateral", - marketAddress: position?.marketInfo?.marketTokenAddress, - initialCollateralTokenAddress: selectedCollateralAddress, - initialCollateralDeltaAmount: collateralDeltaAmount, - targetCollateralAddress: position?.collateralTokenAddress, - collateralDeltaAmount, - sizeDeltaUsd: 0n, - swapPath: [], - isLong: position?.isLong, - orderType, - executionFee: executionFee?.feeTokenAmount, - }; - - const metricId = getPositionOrderMetricId(metricData); - metrics.setPendingEvent(metricId, metricData); - let txnPromise: Promise; if (isDeposit) { @@ -439,7 +437,8 @@ export function PositionEditor(p: Props) { setPendingTxns, setPendingOrder, setPendingPosition, - } + }, + metricId ); } @@ -450,26 +449,8 @@ export function PositionEditor(p: Props) { } txnPromise = txnPromise - .then(() => { - metrics.sendMetric({ - event: "editCollateral.sent", - isError: false, - fields: metrics.getPendingEvent(metricId), - }); - metrics.startTimer(metricId); - - return Promise.resolve(); - }) - .catch((error) => { - metrics.sendMetric({ - event: `editCollateral.${isUserRejectedActionError(error) ? "rejected" : "fail"}`, - isError: true, - message: error.message, - fields: metrics.getPendingEvent(metricId, true), - }); - - throw error; - }); + .then(getTxnSentMetricsHandler(metrics, metricId, metricType)) + .catch(getTxnErrorMetricsHandler(metrics, metricId, metricType)); txnPromise.then(onClose).finally(() => { setIsSubmitting(false); diff --git a/src/components/Synthetics/PositionSeller/PositionSeller.tsx b/src/components/Synthetics/PositionSeller/PositionSeller.tsx index 8f09d5b578..96d896e86a 100644 --- a/src/components/Synthetics/PositionSeller/PositionSeller.tsx +++ b/src/components/Synthetics/PositionSeller/PositionSeller.tsx @@ -64,14 +64,20 @@ import { useLocalizedMap } from "lib/i18n"; import { ExecutionPriceRow } from "../ExecutionPriceRow"; import { PositionSellerAdvancedRows } from "./PositionSellerAdvancedDisplayRows"; -import { makeSelectMarketPriceDecimals } from "context/SyntheticsStateContext/selectors/statsSelectors"; -import "./PositionSeller.scss"; -import { TradeFeesRow } from "../TradeFeesRow/TradeFeesRow"; -import { NetworkFeeRow } from "../NetworkFeeRow/NetworkFeeRow"; import { useMetrics } from "context/MetricsContext/MetricsContext"; -import { getPositionOrderMetricId } from "context/MetricsContext/utils"; +import { + DecreaseOrderMetricParams, + getPositionOrderMetricId, + getTxnErrorMetricsHandler, + getTxnSentMetricsHandler, + sendOrderSubmittedMetric, + sendTxnValidationErrorMetric, +} from "context/MetricsContext/utils"; +import { makeSelectMarketPriceDecimals } from "context/SyntheticsStateContext/selectors/statsSelectors"; import { helperToast } from "lib/helperToast"; -import { isUserRejectedActionError } from "lib/contracts/transactionErrors"; +import { NetworkFeeRow } from "../NetworkFeeRow/NetworkFeeRow"; +import { TradeFeesRow } from "../TradeFeesRow/TradeFeesRow"; +import "./PositionSeller.scss"; export type Props = { setPendingTxns: (txns: any) => void; @@ -278,42 +284,22 @@ export function PositionSeller(p: Props) { } const orderType = isTrigger ? decreaseAmounts?.triggerOrderType : OrderType.MarketDecrease; - let metricType: string; - if (isTrigger) { - metricType = "triggerOrder"; - } else if (decreaseAmounts?.isFullClose) { - metricType = "closePosition"; + + let metricType; + if (orderType === OrderType.LimitDecrease) { + metricType = "takeProfitOrder"; + } else if (orderType === OrderType.StopLossDecrease) { + metricType = "stopLossOrder"; } else { metricType = "decreasePosition"; } - metrics.sendMetric({ - event: `${metricType}.submitted`, - isError: false, - }); - - if ( - !tokensData || - !position || - executionFee?.feeTokenAmount == undefined || - !receiveToken?.address || - receiveUsd === undefined || - decreaseAmounts?.acceptablePrice === undefined || - !signer || - !orderType - ) { - helperToast.error(t`Error submitting order`); - metrics.sendMetric({ - event: `${metricType}.fail`, - isError: true, - message: "Error submitting order, missed data", - }); - return; - } - - const metricData = { + const metricData: DecreaseOrderMetricParams = { metricType, + hasExistingPosition: true, + isFullClose: decreaseAmounts?.isFullClose, place: "positionSeller", + account, marketAddress: position?.marketInfo?.marketTokenAddress, initialCollateralTokenAddress: position?.collateralToken?.address, initialCollateralDeltaAmount: decreaseAmounts?.collateralDeltaAmount, @@ -323,12 +309,30 @@ export function PositionSeller(p: Props) { sizeDeltaUsd: decreaseAmounts?.sizeDeltaUsd, sizeDeltaInTokens: decreaseAmounts?.sizeDeltaInTokens, orderType, - isLong: position.isLong, + isLong: position?.isLong, executionFee: executionFee?.feeTokenAmount, + referralCodeForTxn: userReferralInfo?.referralCodeForTxn, }; const metricId = getPositionOrderMetricId(metricData); - metrics.setPendingEvent(metricId, metricData); + metrics.setCachedMetricData(metricId, metricData); + + sendOrderSubmittedMetric(metrics, metricId, metricType); + + if ( + !tokensData || + !position || + executionFee?.feeTokenAmount == undefined || + !receiveToken?.address || + receiveUsd === undefined || + decreaseAmounts?.acceptablePrice === undefined || + !signer || + !orderType + ) { + helperToast.error(t`Error submitting order`); + sendTxnValidationErrorMetric(metrics, metricId, metricType); + return; + } setIsSubmitting(true); @@ -371,26 +375,8 @@ export function PositionSeller(p: Props) { }, metricId ) - .then(() => { - metrics.sendMetric({ - event: `${metricType}.sent`, - isError: false, - fields: metrics.getPendingEvent(metricId), - }); - metrics.startTimer(metricId); - - return Promise.resolve(); - }) - .catch((error) => { - metrics.sendMetric({ - event: `${metricType}.${isUserRejectedActionError(error) ? "rejected" : "fail"}`, - isError: true, - message: error.message, - fields: metrics.getPendingEvent(metricId, true), - }); - - throw error; - }); + .then(getTxnSentMetricsHandler(metrics, metricId, metricType)) + .catch(getTxnErrorMetricsHandler(metrics, metricId, metricType)); if (subaccount) { onClose(); diff --git a/src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx b/src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx index 3cea549423..c7c6dfc59e 100644 --- a/src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx +++ b/src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx @@ -1,6 +1,16 @@ import { t } from "@lingui/macro"; import { useMetrics } from "context/MetricsContext/MetricsContext"; -import { getPositionOrderMetricId, getSwapOrderMetricId } from "context/MetricsContext/utils"; +import { + DecreaseOrderMetricParams, + getPositionOrderMetricId, + getSwapOrderMetricId, + getTxnErrorMetricsHandler, + getTxnSentMetricsHandler, + IncreaseOrderMetricParams, + sendOrderSubmittedMetric, + sendTxnValidationErrorMetric, + SwapMetricParams, +} from "context/MetricsContext/utils"; import { useSettings } from "context/SettingsContext/SettingsContextProvider"; import { useSubaccount } from "context/SubaccountContext/SubaccountContext"; import { useSyntheticsEvents } from "context/SyntheticsEvents"; @@ -24,14 +34,13 @@ import { import { useSelector } from "context/SyntheticsStateContext/utils"; import { useUserReferralCode } from "domain/referrals"; import { - OrderType, createDecreaseOrderTxn, createIncreaseOrderTxn, createSwapOrderTxn, + OrderType, } from "domain/synthetics/orders"; import { createWrapOrUnwrapTxn } from "domain/synthetics/orders/createWrapOrUnwrapTxn"; import { useChainId } from "lib/chains"; -import { isUserRejectedActionError } from "lib/contracts/transactionErrors"; import { helperToast } from "lib/helperToast"; import { getByKey } from "lib/objects"; import useWallet from "lib/wallets/useWallet"; @@ -83,13 +92,13 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions const onSubmitSwap = useCallback( function onSubmitSwap() { const orderType = isLimit ? OrderType.LimitSwap : OrderType.MarketSwap; - const metricType = isLimit ? "limitSwap" : "swap"; - const metricData = { - metricType, - fromTokenAddress: fromToken?.address, + const metricData: SwapMetricParams = { + metricType: isLimit ? "limitSwap" : "swap", + account, + initialCollateralTokenAddress: fromToken?.address, toTokenAddress: toToken?.address, - fromTokenAmount: swapAmounts?.amountIn, + initialCollateralDeltaAmount: swapAmounts?.amountIn, minOutputAmount: swapAmounts?.minOutputAmount, swapPath: swapAmounts?.swapPathStats?.swapPath, executionFee: executionFee?.feeTokenAmount, @@ -97,14 +106,11 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions orderType, }; + const { metricType } = metricData; const metricId = getSwapOrderMetricId(metricData); - metrics.setPendingEvent(metricId, metricData); - metrics.sendMetric({ - event: `${metricType}.submitted`, - isError: false, - fields: metrics.getPendingEvent(metricId), - }); + metrics.setCachedMetricData(metricId, metricData); + sendOrderSubmittedMetric(metrics, metricId, metricType); if ( !account || @@ -117,16 +123,7 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions typeof allowedSlippage !== "number" ) { helperToast.error(t`Error submitting order`); - metrics.sendMetric({ - event: `${metricType}.fail`, - isError: true, - message: "Error submitting order", - fields: { - isTokensDataLoaded: Boolean(tokensData), - isSignerLoaded: Boolean(signer), - ...metrics.getPendingEvent(metricId, true), - }, - }); + sendTxnValidationErrorMetric(metrics, metricId, metricType); return Promise.resolve(); } @@ -146,41 +143,22 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions setPendingOrder, metricId, }) - .then(() => { - metrics.sendMetric({ - event: `${metricType}.sent`, - isError: false, - fields: metrics.getPendingEvent(metricId), - }); - - metrics.startTimer(metricId); - - return Promise.resolve(); - }) - .catch((error) => { - metrics.sendMetric({ - event: `${metricType}.${isUserRejectedActionError(error) ? "rejected" : "fail"}`, - isError: true, - message: error.message, - fields: metrics.getPendingEvent(metricId, true), - }); - - throw error; - }); + .then(getTxnSentMetricsHandler(metrics, metricId, metricType)) + .catch(getTxnErrorMetricsHandler(metrics, metricId, metricType)); }, [ - metrics, + isLimit, + account, fromToken, toToken, swapAmounts, - account, - tokensData, executionFee, - signer, allowedSlippage, + metrics, + tokensData, + signer, chainId, subaccount, - isLimit, referralCodeForTxn, setPendingTxns, setPendingOrder, @@ -190,48 +168,15 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions const onSubmitIncreaseOrder = useCallback( function onSubmitIncreaseOrder() { const orderType = isLimit ? OrderType.LimitIncrease : OrderType.MarketIncrease; - let metricType: string; - - if (isLimit) { - metricType = "limitOrder"; - } else if (!selectedPosition) { - metricType = "openPosition"; - } else { - metricType = "increasePosition"; - } - - metrics.sendMetric({ - event: `${metricType}.submitted`, - isError: false, - }); - if ( - !tokensData || - !account || - !fromToken || - !collateralToken || - increaseAmounts?.acceptablePrice === undefined || - !executionFee || - !marketInfo || - !signer || - typeof allowedSlippage !== "number" - ) { - helperToast.error(t`Error submitting order`); - metrics.sendMetric({ - event: `${metricType}.fail`, - isError: true, - message: "Error submitting order", - }); - return Promise.resolve(); - } - - const metricData = { - metricType, + const metricData: IncreaseOrderMetricParams = { + metricType: isLimit ? "limitOrder" : "increasePosition", + account, + referralCodeForTxn, + hasExistingPosition: Boolean(selectedPosition), marketAddress: marketInfo?.marketTokenAddress, initialCollateralTokenAddress: fromToken?.address, initialCollateralDeltaAmount: increaseAmounts?.initialCollateralAmount, - targetCollateralAddress: collateralToken?.address, - collateralDeltaAmount: increaseAmounts?.collateralDeltaAmount, swapPath: increaseAmounts?.swapPathStats?.swapPath || [], sizeDeltaUsd: increaseAmounts?.sizeDeltaUsd, sizeDeltaInTokens: increaseAmounts?.sizeDeltaInTokens, @@ -241,20 +186,28 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions orderType, executionFee: executionFee?.feeTokenAmount, }; - + const { metricType } = metricData; const metricId = getPositionOrderMetricId(metricData); - metrics.setPendingEvent(metricId, metricData); + metrics.setCachedMetricData(metricId, metricData); - // DEBUG EVENTS - // [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(() => { - // metrics.sendMetric({ - // event: "increasePosition.submitted", - // isError: false, - // time: Math.floor(Math.random() * (15000 - 1200 + 1)) + 1200, - // fields: metrics.getPendingEvent(metricId), - // }); - // }); + sendOrderSubmittedMetric(metrics, metricId, metricType); + + if ( + !tokensData || + !account || + !fromToken || + !collateralToken || + increaseAmounts?.acceptablePrice === undefined || + !executionFee || + !marketInfo || + !signer || + typeof allowedSlippage !== "number" + ) { + helperToast.error(t`Error submitting order`); + sendTxnValidationErrorMetric(metrics, metricId, metricType); + return Promise.resolve(); + } const commonSecondaryOrderParams = { account, @@ -336,30 +289,13 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions initialCollateralDeltaAmount: entry.order?.initialCollateralDeltaAmount ?? 0n, })), }) - .then(() => { - metrics.sendMetric({ - event: `${metricType}.sent`, - isError: false, - fields: metrics.getPendingEvent(metricId), - }); - - metrics.startTimer(metricId); - - return Promise.resolve(); - }) - .catch((error) => { - metrics.sendMetric({ - event: `${metricType}.${isUserRejectedActionError(error) ? "rejected" : "fail"}`, - isError: true, - message: error.message, - fields: metrics.getPendingEvent(metricId, true), - }); - - throw error; - }); + .then(getTxnSentMetricsHandler(metrics, metricId, metricType)) + .catch(getTxnErrorMetricsHandler(metrics, metricId, metricType)); }, [ isLimit, + account, + referralCodeForTxn, selectedPosition, marketInfo, fromToken, @@ -370,12 +306,10 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions executionFee, metrics, tokensData, - account, signer, allowedSlippage, chainId, subaccount, - referralCodeForTxn, shouldDisableValidationForTesting, setPendingTxns, setPendingOrder, @@ -389,10 +323,32 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions const onSubmitDecreaseOrder = useCallback( function onSubmitDecreaseOrder() { - metrics.sendMetric({ - event: "triggerOrder.submitted", - isError: false, - }); + const metricType = fixedTriggerOrderType === OrderType.LimitDecrease ? "takeProfitOrder" : "stopLossOrder"; + + const metricData: DecreaseOrderMetricParams = { + metricType, + place: "tradeBox", + account, + referralCodeForTxn, + isFullClose: decreaseAmounts?.isFullClose, + hasExistingPosition: Boolean(selectedPosition), + marketAddress: marketInfo?.marketTokenAddress, + initialCollateralTokenAddress: collateralToken?.address, + initialCollateralDeltaAmount: decreaseAmounts?.collateralDeltaAmount, + swapPath: [], + triggerPrice: decreaseAmounts?.triggerPrice, + acceptablePrice: decreaseAmounts?.acceptablePrice, + sizeDeltaUsd: decreaseAmounts?.sizeDeltaUsd, + sizeDeltaInTokens: decreaseAmounts?.sizeDeltaInTokens, + orderType: fixedTriggerOrderType, + isLong, + executionFee: executionFee?.feeTokenAmount, + }; + + const metricId = getPositionOrderMetricId(metricData); + metrics.setCachedMetricData(metricId, metricData); + + sendOrderSubmittedMetric(metrics, metricId, metricType); if ( !account || @@ -408,33 +364,10 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions typeof allowedSlippage !== "number" ) { helperToast.error(t`Error submitting order`); - metrics.sendMetric({ - event: "triggerOrder.fail", - isError: true, - message: "Error submitting order, missed data", - }); + sendTxnValidationErrorMetric(metrics, metricId, metricType); return Promise.resolve(); } - const metricData = { - metricType: "triggerOrder", - place: "tradeBox", - marketAddress: marketInfo?.marketTokenAddress, - initialCollateralTokenAddress: collateralToken?.address, - initialCollateralDeltaAmount: decreaseAmounts.collateralDeltaAmount, - swapPath: [], - triggerPrice: decreaseAmounts?.triggerPrice, - acceptablePrice: decreaseAmounts?.acceptablePrice, - sizeDeltaUsd: decreaseAmounts?.sizeDeltaUsd, - sizeDeltaInTokens: decreaseAmounts?.sizeDeltaInTokens, - orderType: fixedTriggerOrderType, - isLong, - executionFee: executionFee?.feeTokenAmount, - }; - - const metricId = getPositionOrderMetricId(metricData); - metrics.setPendingEvent(metricId, metricData); - return createDecreaseOrderTxn( chainId, signer, @@ -470,29 +403,8 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions }, metricId ) - .then(() => { - metrics.sendMetric({ - event: "triggerOrder.sent", - isError: false, - fields: metrics.getPendingEvent(metricId), - }); - - metrics.startTimer(metricId); - - return Promise.resolve(); - }) - .catch((error) => { - if (!isUserRejectedActionError(error)) { - metrics.sendMetric({ - event: "triggerOrder.fail", - isError: true, - message: error.message, - fields: metrics.getPendingEvent(metricId, true), - }); - } - - throw error; - }); + .then(getTxnSentMetricsHandler(metrics, metricId, metricType)) + .catch(getTxnErrorMetricsHandler(metrics, metricId, metricType)); }, [ account, @@ -507,6 +419,7 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions marketInfo, metrics, referralCodeForTxn, + selectedPosition, setPendingOrder, setPendingPosition, setPendingTxns, diff --git a/src/config/localStorage.ts b/src/config/localStorage.ts index 7dea9eba0a..4e5a90b367 100644 --- a/src/config/localStorage.ts +++ b/src/config/localStorage.ts @@ -50,6 +50,9 @@ export const CHART_TOKEN_SELECTOR_FAVORITE_TOKENS_KEY = "chart-token-selector-fa export const GM_TOKEN_SELECTOR_FILTER_TAB_KEY = "gm-token-selector-filter-tab"; export const GM_TOKEN_SELECTOR_FAVORITE_TOKENS_KEY = "gm-token-selector-favorite-tokens"; +export const METRICS_PENDING_EVENTS_KEY = "metrics-pending-events"; +export const METRICS_TIMERS_KEY = "metrics-timers-key"; + export const getSubgraphUrlKey = (chainId: number, subgraph: string) => `subgraphUrl:${chainId}:${subgraph}`; export function getSyntheticsDepositIndexTokenKey(chainId: number) { diff --git a/src/context/MetricsContext/MetricsContext.tsx b/src/context/MetricsContext/MetricsContext.tsx index 60db5312ea..65931b48ae 100644 --- a/src/context/MetricsContext/MetricsContext.tsx +++ b/src/context/MetricsContext/MetricsContext.tsx @@ -1,23 +1,34 @@ import { isDevelopment } from "config/env"; +import { METRICS_PENDING_EVENTS_KEY as CACHED_METRICS_DATA_KEY, METRICS_TIMERS_KEY } from "config/localStorage"; +import { useSettings } from "context/SettingsContext/SettingsContextProvider"; import { useSubaccountAddress } from "context/SubaccountContext/SubaccountContext"; import { useOracleKeeperFetcher } from "domain/synthetics/tokens"; import { useChainId } from "lib/chains"; +import { JSONWithBNParse, JSONWwithBNStringify } from "lib/jsonWithBigint"; import { getAppVersion } from "lib/version"; import { getWalletNames } from "lib/wallets/getWalletNames"; -import { Context, PropsWithChildren, useMemo, useRef } from "react"; -import { createContext, useContextSelector } from "use-context-selector"; +import useIsMetamaskMobile from "lib/wallets/useIsMetamaskMobile"; import mapValues from "lodash/mapValues"; +import { Context, PropsWithChildren, useMemo } from "react"; +import { createContext, useContextSelector } from "use-context-selector"; +import { MetricData, MetricEventType } from "./utils"; + +const MAX_METRICS_STORE_TIME = 1000 * 60 * 30; // 30 min + +type CachedMetricData = MetricData & { _metricDataCreated: number; metricId: string }; +type CachedMetricsData = { [key: string]: CachedMetricData }; +type Timers = { [key: string]: number }; export type MetricsContextType = { sendMetric: (params: { - event: string; - fields?: Record; + event: MetricEventType; + fields?: MetricData; time?: number; isError: boolean; message?: string; }) => void; - setPendingEvent: (metricId: string, eventData: T) => T; - getPendingEvent: (metricId: string, clear?: boolean) => any; + setCachedMetricData: (metricId: string, metricData: MetricData) => void; + getCachedMetricData: (metricId: string, clear?: boolean) => CachedMetricData | undefined; startTimer: (metricId: string) => void; getTime: (metricId: string, clear?: boolean) => number | undefined; }; @@ -28,40 +39,64 @@ export function MetricsContextProvider({ children }: PropsWithChildren) { const { chainId } = useChainId(); const fetcher = useOracleKeeperFetcher(chainId); const subaccountAddress = useSubaccountAddress(); - - const pendingEvents = useRef({}); - const timers = useRef({}); + const { showDebugValues } = useSettings(); + const isMobileMetamask = useIsMetamaskMobile(); const value: MetricsContextType = useMemo(() => { - const setPendingEvent = (metricId: string, eventData: any) => { - pendingEvents.current[metricId] = { metricId, ...eventData }; + const setCachedMetricData = (metricId: string, metricData: MetricData) => { + const cachedMetricsData = localStorage.getItem(CACHED_METRICS_DATA_KEY); - return eventData; + const metricsData: CachedMetricsData = cachedMetricsData ? JSONWithBNParse(cachedMetricsData) : {}; + + metricsData[metricId] = { metricId, _metricDataCreated: Date.now(), ...metricData }; + + localStorage.setItem(CACHED_METRICS_DATA_KEY, JSONWwithBNStringify(clearOldMetrics(metricsData))); }; - const getPendingEvent = (metricId: string, clear?: boolean) => { - const event = pendingEvents.current[metricId]; + const getCachedMetricData = (metricId: string, clear?: boolean): CachedMetricData | undefined => { + const cachedMetricsData = localStorage.getItem(CACHED_METRICS_DATA_KEY); + + if (!cachedMetricsData) { + return undefined; + } + + const metricsData = JSONWithBNParse(cachedMetricsData); + + const event = metricsData[metricId]; if (clear) { - pendingEvents.current[metricId] = undefined; + metricsData[metricId] = undefined; + localStorage.setItem(CACHED_METRICS_DATA_KEY, JSONWwithBNStringify(clearOldMetrics(metricsData))); } return event; }; const startTimer = (metricId: string) => { - timers.current[metricId] = Date.now(); + const storedTimers = localStorage.getItem(METRICS_TIMERS_KEY); + const timers = storedTimers ? JSON.parse(storedTimers) : {}; + timers[metricId] = Date.now(); + + localStorage.setItem(METRICS_TIMERS_KEY, JSON.stringify(clearOldTimers(timers))); }; const getTime = (metricId: string, clear?: boolean) => { - const time = timers.current[metricId]; + const storedTimers = localStorage.getItem(METRICS_TIMERS_KEY); + + if (!storedTimers) { + return undefined; + } + + const timers = JSON.parse(storedTimers); + const time = timers[metricId]; if (!time) { return undefined; } if (clear) { - timers.current[metricId] = undefined; + timers[metricId] = undefined; + localStorage.setItem(METRICS_TIMERS_KEY, JSON.stringify(clearOldTimers(timers))); } return Date.now() - time; @@ -77,14 +112,18 @@ export function MetricsContextProvider({ children }: PropsWithChildren) { const { time, isError, fields, message, event } = params; const wallets = await getWalletNames(); - // eslint-disable-next-line no-console - console.log("sendMetric", { - time, - isError, - fields, - message, - event, - }); + if (showDebugValues) { + // eslint-disable-next-line no-console + console.log("sendMetric", { + event, + is1ct: Boolean(subaccountAddress), + wallet: wallets.current, + time, + isError, + fields, + message, + }); + } await fetcher.fetchPostReport2({ is1ct: Boolean(subaccountAddress), @@ -96,18 +135,23 @@ export function MetricsContextProvider({ children }: PropsWithChildren) { version: getAppVersion(), isError, time, - customFields: serializeCustomFields({ ...fields, message }), + customFields: { + ...serializeCustomFields(fields), + message, + isMobileMetamask, + wallets, + }, }); } return { sendMetric, - setPendingEvent, - getPendingEvent, + setCachedMetricData, + getCachedMetricData, startTimer, getTime, }; - }, [fetcher, subaccountAddress]); + }, [fetcher, isMobileMetamask, showDebugValues, subaccountAddress]); return {children}; } @@ -128,10 +172,34 @@ function serializeCustomFields(fields: Record) { result = v.toString(); } - if (result.length && result.lenth > 150) { + if (typeof result === "string" && result.length > 150) { result = result.slice(0, 150); } return result; }); } + +function clearOldMetrics(metricsData: CachedMetricsData) { + const result: { [key: string]: CachedMetricData } = {}; + + Object.keys(metricsData).forEach((key) => { + if (metricsData[key] && Date.now() - metricsData[key]._metricDataCreated < MAX_METRICS_STORE_TIME) { + result[key] = metricsData[key]; + } + }); + + return result; +} + +function clearOldTimers(timers: Timers) { + const result: { [key: string]: number } = {}; + + Object.keys(timers).forEach((key) => { + if (Date.now() - timers[key] < MAX_METRICS_STORE_TIME) { + result[key] = timers[key]; + } + }); + + return result; +} diff --git a/src/context/MetricsContext/errorReporting.ts b/src/context/MetricsContext/errorReporting.ts index 25bbeae341..3f46d20061 100644 --- a/src/context/MetricsContext/errorReporting.ts +++ b/src/context/MetricsContext/errorReporting.ts @@ -1,86 +1,18 @@ import CustomErrors from "abis/CustomErrors.json"; import { isDevelopment, isLocal } from "config/env"; +import { SHOW_DEBUG_VALUES_KEY } from "config/localStorage"; import cryptoJs from "crypto-js"; import { extractDataFromError } from "domain/synthetics/orders/simulateExecuteOrderTxn"; import { OracleFetcher, useOracleKeeperFetcher } from "domain/synthetics/tokens"; import { ethers } from "ethers"; -import { useCallback, useEffect, useMemo, useRef } from "react"; +import { useEffect } from "react"; import { extractError } from "../../lib/contracts/transactionErrors"; import { useLocalStorageSerializeKey } from "../../lib/localStorage"; -import { SHOW_DEBUG_VALUES_KEY } from "config/localStorage"; -import { useSubaccountAddress } from "context/SubaccountContext/SubaccountContext"; -import useIsMetamaskMobile from "../../lib/wallets/useIsMetamaskMobile"; -import { useChainId } from "../../lib/chains"; -import { getWalletNames } from "../../lib/wallets/getWalletNames"; import { getAppVersion } from "../../lib/version"; +import { getWalletNames } from "../../lib/wallets/getWalletNames"; const IGNORE_ERROR_MESSAGES = ["user rejected action", "failed to fetch"]; -export function useUiMetrics() { - const { chainId } = useChainId(); - const fetcher = useOracleKeeperFetcher(chainId); - const subaccountAddress = useSubaccountAddress(); - const isMetamaskMobile = useIsMetamaskMobile(); - - const pendingEvents = useRef({}); - const timers = useRef({}); - - const setPendingEvent = useCallback((key: string, eventData: any) => { - pendingEvents[key] = eventData; - return eventData; - }, []); - - const getPendingEvent = useCallback((key: string, clear?: boolean) => { - const event = pendingEvents[key]; - - if (clear) { - pendingEvents[key] = undefined; - } - - return event; - }, []); - - const sendMetric = useCallback( - async function sendMetric(params: { - event: string; - fields?: any; - time?: number; - isError: boolean; - message?: string; - }) { - const { time, isError, fields, message, event } = params; - const wallets = await getWalletNames(); - - const body = { - is1ct: Boolean(subaccountAddress), - isDev: isDevelopment(), - host: window.location.host, - url: window.location.href, - wallet: wallets.current, - event: event, - version: getAppVersion(), - isError, - time, - isMetamaskMobile, - message, - customFields: fields, - }; - - fetcher.fetchPostReport2(body); - }, - [fetcher, isMetamaskMobile, subaccountAddress] - ); - - return useMemo( - () => ({ - sendMetric, - setPendingEvent, - getPendingEvent, - }), - [getPendingEvent, sendMetric, setPendingEvent] - ); -} - export function useErrorReporting(chainId: number) { const [showDebugValues] = useLocalStorageSerializeKey(SHOW_DEBUG_VALUES_KEY, false); const fetcher = useOracleKeeperFetcher(chainId); diff --git a/src/context/MetricsContext/utils.ts b/src/context/MetricsContext/utils.ts index f5602c5e6c..ae91e16cd1 100644 --- a/src/context/MetricsContext/utils.ts +++ b/src/context/MetricsContext/utils.ts @@ -1,45 +1,218 @@ import { OrderType } from "domain/synthetics/orders"; +import { extractError, isUserRejectedActionError, TxError } from "lib/contracts/transactionErrors"; +import { MetricsContextType } from "./MetricsContext"; -export function getSwapOrderMetricId(data: { - fromTokenAddress?: string; - toTokenAddress?: string; - fromTokenAmount?: bigint; - minOutputAmount?: bigint; - swapPath?: string[]; - executionFee?: bigint; - orderType?: OrderType; +export type MetricEventType = OrderEventType | "positionsListLoad.success"; +export type MetricData = OrderMetricData | OrderWsEventMetricData | PendingTxnErrorMetricData; + +export type OrderEventType = `${OrderMetricType}.${OrderStageType}`; +export type OrderStageType = "submitted" | "sent" | "created" | "executed" | "cancelled" | "rejected" | "failed"; + +export type OrderMetricType = + | SwapMetricParams["metricType"] + | IncreaseOrderMetricParams["metricType"] + | DecreaseOrderMetricParams["metricType"] + | EditCollateralMetricParams["metricType"] + | "unknownTxn"; + +export type OrderMetricData = + | SwapMetricParams + | IncreaseOrderMetricParams + | DecreaseOrderMetricParams + | EditCollateralMetricParams; + +export type OrderWsEventMetricData = (SwapMetricParams | IncreaseOrderMetricParams | DecreaseOrderMetricParams) & { + key: string; + txnHash: string; +}; + +export type PendingTxnErrorMetricData = { + metricType: OrderMetricType; + txnHash: string; +}; + +export type SwapMetricParams = { + metricType: "swap" | "limitSwap"; + account: string | undefined; + initialCollateralTokenAddress: string | undefined; + toTokenAddress: string | undefined; + initialCollateralDeltaAmount: bigint | undefined; + minOutputAmount: bigint | undefined; + swapPath: string[] | undefined; + executionFee: bigint | undefined; + allowedSlippage: number | undefined; + orderType: OrderType | undefined; +}; + +export type PositionOrderMetricParams = { + account: string | undefined; + referralCodeForTxn: string | undefined; + hasExistingPosition: boolean | undefined; + marketAddress: string | undefined; + initialCollateralTokenAddress: string | undefined; + initialCollateralDeltaAmount: bigint | undefined; + swapPath: string[] | undefined; + sizeDeltaUsd: bigint | undefined; + sizeDeltaInTokens: bigint | undefined; + triggerPrice: bigint | undefined; + acceptablePrice: bigint | undefined; + isLong: boolean | undefined; + orderType: OrderType | undefined; + executionFee: bigint | undefined; +}; + +export type IncreaseOrderMetricParams = { + metricType: "increasePosition" | "limitOrder"; +} & PositionOrderMetricParams; + +export type DecreaseOrderMetricParams = { + metricType: "decreasePosition" | "takeProfitOrder" | "stopLossOrder"; + place: "tradeBox" | "positionSeller"; + isFullClose: boolean | undefined; +} & PositionOrderMetricParams; + +export type EditCollateralMetricParams = { + metricType: "depositCollateral" | "withdrawCollateral"; + account: string | undefined; + marketAddress: string | undefined; + initialCollateralTokenAddress: string | undefined; + initialCollateralDeltaAmount: bigint | undefined; + swapPath: []; + isLong: boolean | undefined; + orderType: OrderType | undefined; + executionFee: bigint | undefined; +}; + +export function getMetricTypeByOrderType(p: { + orderType: OrderType; + sizeDeltaUsd: bigint | undefined; +}): OrderMetricType { + const { orderType, sizeDeltaUsd } = p; + + if (orderType === OrderType.MarketSwap) { + return "swap"; + } + + if (orderType === OrderType.LimitSwap) { + return "limitSwap"; + } + + if (orderType === OrderType.MarketIncrease) { + if (sizeDeltaUsd === 0n) { + return "depositCollateral"; + } + + return "increasePosition"; + } + + if (orderType === OrderType.MarketDecrease) { + if (sizeDeltaUsd === 0n) { + return "withdrawCollateral"; + } + + return "decreasePosition"; + } + + if (orderType === OrderType.LimitIncrease) { + return "limitOrder"; + } + + if (orderType === OrderType.LimitDecrease) { + return "takeProfitOrder"; + } + + return "stopLossOrder"; +} + +export function getSwapOrderMetricId(p: { + account: string | undefined; + initialCollateralTokenAddress: string | undefined; + initialCollateralDeltaAmount: bigint | undefined; + swapPath: string[] | undefined; + executionFee: bigint | undefined; + orderType: OrderType | undefined; }) { return [ "swap", - data.fromTokenAddress, - data.toTokenAddress, - data.fromTokenAmount?.toString(), - data.minOutputAmount?.toString(), - data.swapPath?.join("-"), - data.executionFee?.toString(), - data.orderType, + p.account || "account", + p.initialCollateralTokenAddress || "initialColltateralTokenAddress", + p.initialCollateralDeltaAmount?.toString() || "initialCollateralDeltaAmount", + p.swapPath?.join("-") || "swapPath", + p.executionFee?.toString() || "executionFee", + p.orderType || "orderType", ].join(":"); } -export function getPositionOrderMetricId(data: { - marketAddress: string; - initialCollateralTokenAddress: string; - initialCollateralDeltaAmount: bigint; - swapPath: string[]; - sizeDeltaUsd: bigint; - isLong: boolean; - orderType: OrderType; - executionFee: bigint; +export function getPositionOrderMetricId(p: { + account: string | undefined; + marketAddress: string | undefined; + initialCollateralTokenAddress: string | undefined; + initialCollateralDeltaAmount: bigint | undefined; + swapPath: string[] | undefined; + sizeDeltaUsd: bigint | undefined; + isLong: boolean | undefined; + orderType: OrderType | undefined; + executionFee: bigint | undefined; }) { return [ "position", - data.marketAddress, - data.initialCollateralTokenAddress, - data.initialCollateralDeltaAmount?.toString(), - data.swapPath?.join("-"), - data.sizeDeltaUsd?.toString(), - data.isLong, - data.orderType, - data.executionFee?.toString(), + p.account || "account", + p.marketAddress || "marketAddress", + p.initialCollateralTokenAddress || "initialCollateralTokenAddress", + p.initialCollateralDeltaAmount?.toString() || "initialCollateralDeltaAmount", + p.swapPath?.join("-") || "swapPath", + p.sizeDeltaUsd?.toString() || "sizeDeltaUsd", + p.isLong || "isLong", + p.orderType || "orderType", + p.executionFee?.toString() || "executionFee", ].join(":"); } + +export function sendOrderSubmittedMetric(metrics: MetricsContextType, metricId: string, metricType: OrderMetricType) { + metrics.sendMetric({ + event: `${metricType}.submitted`, + isError: false, + fields: metrics.getCachedMetricData(metricId), + }); +} + +export function sendTxnValidationErrorMetric( + metrics: MetricsContextType, + metricId: string, + metricType: OrderMetricType +) { + metrics.sendMetric({ + event: `${metricType}.failed`, + isError: true, + message: "Error submitting order, missed data", + fields: metrics.getCachedMetricData(metricId, true), + }); +} + +export function getTxnSentMetricsHandler(metrics: MetricsContextType, metricId: string, metricType: OrderMetricType) { + return () => { + metrics.sendMetric({ + event: `${metricType}.sent`, + isError: false, + time: metrics.getTime(metricId), + fields: metrics.getCachedMetricData(metricId), + }); + + return Promise.resolve(); + }; +} + +export function getTxnErrorMetricsHandler(metrics: MetricsContextType, metricId: string, metricType: OrderMetricType) { + return (error: Error | TxError) => { + const [message] = extractError(error); + + metrics.sendMetric({ + event: `${metricType}.${isUserRejectedActionError(error as Error) ? "rejected" : "failed"}`, + isError: true, + message, + fields: metrics.getCachedMetricData(metricId, true), + }); + + throw error; + }; +} diff --git a/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx b/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx index 3e77909be1..92bbb76bbf 100644 --- a/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx +++ b/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -45,7 +45,12 @@ import { WithdrawalStatuses, } from "./types"; import { useMetrics } from "context/MetricsContext/MetricsContext"; -import { getPositionOrderMetricId, getSwapOrderMetricId } from "context/MetricsContext/utils"; +import { + getMetricTypeByOrderType, + getPositionOrderMetricId, + getSwapOrderMetricId, + OrderWsEventMetricData, +} from "context/MetricsContext/utils"; export const SyntheticsEventsContext = createContext({}); @@ -107,14 +112,19 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) } const metricId = isSwapOrderType(data.orderType) ? getSwapOrderMetricId(data) : getPositionOrderMetricId(data); - const metricData = metrics.getPendingEvent(metricId); - const metricType = metricData?.metricType || "unknownOrder"; + const metricData = metrics.getCachedMetricData(metricId); + const metricType = metricData?.metricType || getMetricTypeByOrderType(data); metrics.sendMetric({ event: `${metricType}.created`, isError: false, time: metrics.getTime(metricId), - fields: metricData, + fields: { + ...(metricData || {}), + metricType, + key: data.key, + txnHash: txnParams.transactionHash, + } as OrderWsEventMetricData, }); setOrderStatuses((old) => @@ -161,14 +171,19 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) ? getSwapOrderMetricId(order) : getPositionOrderMetricId(order); - const metricData = metrics.getPendingEvent(metricId, true); - const metricType = metricData?.metricType || "unknownOrder"; + const metricData = metrics.getCachedMetricData(metricId, true); + const metricType = metricData?.metricType || getMetricTypeByOrderType(order); metrics.sendMetric({ event: `${metricType}.executed`, isError: false, time: metrics.getTime(metricId, true), - fields: metricData, + fields: { + ...(metricData || {}), + metricType, + key, + txnHash: txnParams.transactionHash, + } as OrderWsEventMetricData, }); } @@ -209,15 +224,20 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) ? getSwapOrderMetricId(order) : getPositionOrderMetricId(order); - const metricData = metrics.getPendingEvent(metricId, true); - const metricType = metricData?.metricType || "unknownOrder"; + const metricData = metrics.getCachedMetricData(metricId, true); + const metricType = metricData?.metricType || getMetricTypeByOrderType(order); metrics.sendMetric({ - event: `${metricType}.fail`, + event: `${metricType}.failed`, isError: true, - message: `Order cancelled, key: ${order.key}, txn: ${txnParams.transactionHash}`, + message: `Order cancelled`, time: metrics.getTime(metricId, true), - fields: metricData, + fields: { + ...(metricData || {}), + metricType, + key, + txnHash: txnParams.transactionHash, + } as OrderWsEventMetricData, }); } @@ -485,7 +505,7 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) useEffect( function subscribe() { - if (hasV2LostFocus || !wsProvider || !currentAccount) { + if (hasV2LostFocus || !wsProvider || !currentAccount || !metrics) { return; } @@ -495,7 +515,7 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) unsubscribe(); }; }, - [chainId, currentAccount, hasV2LostFocus, wsProvider] + [chainId, currentAccount, hasV2LostFocus, metrics, wsProvider] ); const contextState: SyntheticsEventsContextType = useMemo(() => { diff --git a/src/lib/contracts/transactionErrors.tsx b/src/lib/contracts/transactionErrors.tsx index 3a70df1fde..34c0bf3488 100644 --- a/src/lib/contracts/transactionErrors.tsx +++ b/src/lib/contracts/transactionErrors.tsx @@ -46,7 +46,7 @@ const TX_ERROR_PATTERNS: { [key: string]: ErrorPattern[] } = { ], }; -type TxError = { +export type TxError = { message?: string; code?: number; data?: any; diff --git a/src/lib/jsonWithBigint.ts b/src/lib/jsonWithBigint.ts new file mode 100644 index 0000000000..5e66c4c3aa --- /dev/null +++ b/src/lib/jsonWithBigint.ts @@ -0,0 +1,25 @@ +export const JSONWwithBNStringify = (data) => { + // eslint-disable-next-line no-useless-escape + const bigInts = /([\[:])?"(-?\d+)n"([,\}\]])/g; + const preliminaryJSON = JSON.stringify(data, (_, value) => + typeof value === "bigint" ? value.toString() + "n" : value + ); + const finalJSON = preliminaryJSON.replace(bigInts, "$1$2$3"); + + return finalJSON; +}; + +export const JSONWithBNParse = (json) => { + const numbersBiggerThanMaxInt = + // eslint-disable-next-line no-useless-escape + /(?<=[^\\]":[\[]?|[^\\]":\[.*[^\.\d*])(-?\d{17,}|-?(?:[9](?:[1-9]07199254740991|0[1-9]7199254740991|00[8-9]199254740991|007[2-9]99254740991|007199[3-9]54740991|0071992[6-9]4740991|00719925[5-9]740991|007199254[8-9]40991|0071992547[5-9]0991|00719925474[1-9]991|00719925474099[2-9])))(?=,|\}[^"]|\][^"])/g; + const serializedData = json.replace(numbersBiggerThanMaxInt, `"$1n"`); + + return JSON.parse(serializedData, (_, value) => { + const isCustomFormatBigInt = typeof value === "string" && Boolean(value.match(/^-?\d+n$/)); + + if (isCustomFormatBigInt) return BigInt(value.substring(0, value.length - 1)); + + return value; + }); +}; From 3d54ffc5bc46a86d5d528f9d1825e4ed6117f849 Mon Sep 17 00:00:00 2001 From: Divhead Date: Mon, 12 Aug 2024 19:15:41 +0300 Subject: [PATCH 13/62] fix review --- src/context/MetricsContext/MetricsContext.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/context/MetricsContext/MetricsContext.tsx b/src/context/MetricsContext/MetricsContext.tsx index 65931b48ae..255430d8c9 100644 --- a/src/context/MetricsContext/MetricsContext.tsx +++ b/src/context/MetricsContext/MetricsContext.tsx @@ -164,8 +164,8 @@ export function useMetrics() { return useMetricsSelector((s) => s); } -function serializeCustomFields(fields: Record) { - return mapValues(fields, (v) => { +function serializeCustomFields(fields: MetricData) { + return mapValues(fields, (v: any) => { let result; if (typeof v === "bigint") { From b3b089e14103ea06abe12eb376c4198c7005441d Mon Sep 17 00:00:00 2001 From: Divhead Date: Mon, 12 Aug 2024 19:35:29 +0300 Subject: [PATCH 14/62] clean --- src/App/App.tsx | 2 +- .../PositionEditor/PositionEditor.tsx | 4 +- .../PositionSeller/PositionSeller.tsx | 4 +- .../hooks/useTradeboxTransactions.tsx | 10 +-- src/context/MetricsContext/MetricsContext.tsx | 2 +- src/context/MetricsContext/types.ts | 82 +++++++++++++++++++ src/context/MetricsContext/utils.ts | 82 +------------------ .../SyntheticsEventsProvider.tsx | 8 +- 8 files changed, 95 insertions(+), 99 deletions(-) create mode 100644 src/context/MetricsContext/types.ts diff --git a/src/App/App.tsx b/src/App/App.tsx index d5b4a1e243..888d942ccb 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -78,7 +78,7 @@ function App() { if (pendingTxn.metricId) { const metricData = metrics.getCachedMetricData(pendingTxn.metricId, true); - const metricType = metricData?.metricType || "unknownTxn"; + const metricType = metricData?.metricType || "unknownOrder"; metrics?.sendMetric({ event: `${metricType}.failed`, diff --git a/src/components/Synthetics/PositionEditor/PositionEditor.tsx b/src/components/Synthetics/PositionEditor/PositionEditor.tsx index 6edbd5f905..af87510355 100644 --- a/src/components/Synthetics/PositionEditor/PositionEditor.tsx +++ b/src/components/Synthetics/PositionEditor/PositionEditor.tsx @@ -78,13 +78,13 @@ import { usePositionEditorFees } from "./hooks/usePositionEditorFees"; import { useMetrics } from "context/MetricsContext/MetricsContext"; import { - EditCollateralMetricParams, getPositionOrderMetricId, getTxnErrorMetricsHandler, getTxnSentMetricsHandler, sendOrderSubmittedMetric, sendTxnValidationErrorMetric, } from "context/MetricsContext/utils"; +import { EditCollateralMetricData } from "context/MetricsContext/types"; import { makeSelectMarketPriceDecimals } from "context/SyntheticsStateContext/selectors/statsSelectors"; import { useSelector } from "context/SyntheticsStateContext/utils"; import { helperToast } from "lib/helperToast"; @@ -334,7 +334,7 @@ export function PositionEditor(p: Props) { const orderType = isDeposit ? OrderType.MarketIncrease : OrderType.MarketDecrease; const metricType = isDeposit ? "depositCollateral" : "withdrawCollateral"; - const metricData: EditCollateralMetricParams = { + const metricData: EditCollateralMetricData = { metricType, account, marketAddress: position?.marketInfo?.marketTokenAddress, diff --git a/src/components/Synthetics/PositionSeller/PositionSeller.tsx b/src/components/Synthetics/PositionSeller/PositionSeller.tsx index e90d885245..ad9f842702 100644 --- a/src/components/Synthetics/PositionSeller/PositionSeller.tsx +++ b/src/components/Synthetics/PositionSeller/PositionSeller.tsx @@ -66,7 +66,6 @@ import { PositionSellerAdvancedRows } from "./PositionSellerAdvancedDisplayRows" import { useMetrics } from "context/MetricsContext/MetricsContext"; import { - DecreaseOrderMetricParams, getPositionOrderMetricId, getTxnErrorMetricsHandler, getTxnSentMetricsHandler, @@ -78,6 +77,7 @@ import { helperToast } from "lib/helperToast"; import { NetworkFeeRow } from "../NetworkFeeRow/NetworkFeeRow"; import { TradeFeesRow } from "../TradeFeesRow/TradeFeesRow"; import "./PositionSeller.scss"; +import { DecreaseOrderMetricData } from "context/MetricsContext/types"; export type Props = { setPendingTxns: (txns: any) => void; @@ -294,7 +294,7 @@ export function PositionSeller(p: Props) { metricType = "decreasePosition"; } - const metricData: DecreaseOrderMetricParams = { + const metricData: DecreaseOrderMetricData = { metricType, hasExistingPosition: true, isFullClose: decreaseAmounts?.isFullClose, diff --git a/src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx b/src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx index c7c6dfc59e..e159b7550a 100644 --- a/src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx +++ b/src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx @@ -1,15 +1,12 @@ import { t } from "@lingui/macro"; import { useMetrics } from "context/MetricsContext/MetricsContext"; import { - DecreaseOrderMetricParams, getPositionOrderMetricId, getSwapOrderMetricId, getTxnErrorMetricsHandler, getTxnSentMetricsHandler, - IncreaseOrderMetricParams, sendOrderSubmittedMetric, sendTxnValidationErrorMetric, - SwapMetricParams, } from "context/MetricsContext/utils"; import { useSettings } from "context/SettingsContext/SettingsContextProvider"; import { useSubaccount } from "context/SubaccountContext/SubaccountContext"; @@ -47,6 +44,7 @@ import useWallet from "lib/wallets/useWallet"; import { useCallback } from "react"; import { useRequiredActions } from "./useRequiredActions"; import { useTPSLSummaryExecutionFee } from "./useTPSLSummaryExecutionFee"; +import { DecreaseOrderMetricData, IncreaseOrderMetricData, SwapMetricData } from "context/MetricsContext/types"; interface TradeboxTransactionsProps { setPendingTxns: (txns: any) => void; @@ -93,7 +91,7 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions function onSubmitSwap() { const orderType = isLimit ? OrderType.LimitSwap : OrderType.MarketSwap; - const metricData: SwapMetricParams = { + const metricData: SwapMetricData = { metricType: isLimit ? "limitSwap" : "swap", account, initialCollateralTokenAddress: fromToken?.address, @@ -169,7 +167,7 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions function onSubmitIncreaseOrder() { const orderType = isLimit ? OrderType.LimitIncrease : OrderType.MarketIncrease; - const metricData: IncreaseOrderMetricParams = { + const metricData: IncreaseOrderMetricData = { metricType: isLimit ? "limitOrder" : "increasePosition", account, referralCodeForTxn, @@ -325,7 +323,7 @@ export function useTradeboxTransactions({ setPendingTxns }: TradeboxTransactions function onSubmitDecreaseOrder() { const metricType = fixedTriggerOrderType === OrderType.LimitDecrease ? "takeProfitOrder" : "stopLossOrder"; - const metricData: DecreaseOrderMetricParams = { + const metricData: DecreaseOrderMetricData = { metricType, place: "tradeBox", account, diff --git a/src/context/MetricsContext/MetricsContext.tsx b/src/context/MetricsContext/MetricsContext.tsx index 255430d8c9..42cf02bdc6 100644 --- a/src/context/MetricsContext/MetricsContext.tsx +++ b/src/context/MetricsContext/MetricsContext.tsx @@ -11,7 +11,7 @@ import useIsMetamaskMobile from "lib/wallets/useIsMetamaskMobile"; import mapValues from "lodash/mapValues"; import { Context, PropsWithChildren, useMemo } from "react"; import { createContext, useContextSelector } from "use-context-selector"; -import { MetricData, MetricEventType } from "./utils"; +import { MetricData, MetricEventType } from "./types"; const MAX_METRICS_STORE_TIME = 1000 * 60 * 30; // 30 min diff --git a/src/context/MetricsContext/types.ts b/src/context/MetricsContext/types.ts new file mode 100644 index 0000000000..6c29cd8439 --- /dev/null +++ b/src/context/MetricsContext/types.ts @@ -0,0 +1,82 @@ +import { OrderType } from "domain/synthetics/orders"; + +export type MetricEventType = OrderEventType | "positionsListLoad.success"; +export type MetricData = OrderMetricData | OrderWsEventMetricData | PendingTxnErrorMetricData; + +export type OrderEventType = `${OrderMetricType}.${OrderStageType}`; +export type OrderStageType = "submitted" | "sent" | "created" | "executed" | "cancelled" | "rejected" | "failed"; + +export type OrderMetricType = + | SwapMetricData["metricType"] + | IncreaseOrderMetricData["metricType"] + | DecreaseOrderMetricData["metricType"] + | EditCollateralMetricData["metricType"] + | "unknownOrder"; + +export type OrderMetricData = + | SwapMetricData + | IncreaseOrderMetricData + | DecreaseOrderMetricData + | EditCollateralMetricData; + +export type OrderWsEventMetricData = (SwapMetricData | IncreaseOrderMetricData | DecreaseOrderMetricData) & { + key: string; + txnHash: string; +}; + +export type PendingTxnErrorMetricData = { + metricType: OrderMetricType; + txnHash: string; +}; + +export type SwapMetricData = { + metricType: "swap" | "limitSwap"; + account: string | undefined; + initialCollateralTokenAddress: string | undefined; + toTokenAddress: string | undefined; + initialCollateralDeltaAmount: bigint | undefined; + minOutputAmount: bigint | undefined; + swapPath: string[] | undefined; + executionFee: bigint | undefined; + allowedSlippage: number | undefined; + orderType: OrderType | undefined; +}; + +export type PositionOrderMetricParams = { + account: string | undefined; + referralCodeForTxn: string | undefined; + hasExistingPosition: boolean | undefined; + marketAddress: string | undefined; + initialCollateralTokenAddress: string | undefined; + initialCollateralDeltaAmount: bigint | undefined; + swapPath: string[] | undefined; + sizeDeltaUsd: bigint | undefined; + sizeDeltaInTokens: bigint | undefined; + triggerPrice: bigint | undefined; + acceptablePrice: bigint | undefined; + isLong: boolean | undefined; + orderType: OrderType | undefined; + executionFee: bigint | undefined; +}; + +export type IncreaseOrderMetricData = { + metricType: "increasePosition" | "limitOrder"; +} & PositionOrderMetricParams; + +export type DecreaseOrderMetricData = { + metricType: "decreasePosition" | "takeProfitOrder" | "stopLossOrder"; + place: "tradeBox" | "positionSeller"; + isFullClose: boolean | undefined; +} & PositionOrderMetricParams; + +export type EditCollateralMetricData = { + metricType: "depositCollateral" | "withdrawCollateral"; + account: string | undefined; + marketAddress: string | undefined; + initialCollateralTokenAddress: string | undefined; + initialCollateralDeltaAmount: bigint | undefined; + swapPath: []; + isLong: boolean | undefined; + orderType: OrderType | undefined; + executionFee: bigint | undefined; +}; diff --git a/src/context/MetricsContext/utils.ts b/src/context/MetricsContext/utils.ts index ae91e16cd1..882684bb9e 100644 --- a/src/context/MetricsContext/utils.ts +++ b/src/context/MetricsContext/utils.ts @@ -1,87 +1,7 @@ import { OrderType } from "domain/synthetics/orders"; import { extractError, isUserRejectedActionError, TxError } from "lib/contracts/transactionErrors"; import { MetricsContextType } from "./MetricsContext"; - -export type MetricEventType = OrderEventType | "positionsListLoad.success"; -export type MetricData = OrderMetricData | OrderWsEventMetricData | PendingTxnErrorMetricData; - -export type OrderEventType = `${OrderMetricType}.${OrderStageType}`; -export type OrderStageType = "submitted" | "sent" | "created" | "executed" | "cancelled" | "rejected" | "failed"; - -export type OrderMetricType = - | SwapMetricParams["metricType"] - | IncreaseOrderMetricParams["metricType"] - | DecreaseOrderMetricParams["metricType"] - | EditCollateralMetricParams["metricType"] - | "unknownTxn"; - -export type OrderMetricData = - | SwapMetricParams - | IncreaseOrderMetricParams - | DecreaseOrderMetricParams - | EditCollateralMetricParams; - -export type OrderWsEventMetricData = (SwapMetricParams | IncreaseOrderMetricParams | DecreaseOrderMetricParams) & { - key: string; - txnHash: string; -}; - -export type PendingTxnErrorMetricData = { - metricType: OrderMetricType; - txnHash: string; -}; - -export type SwapMetricParams = { - metricType: "swap" | "limitSwap"; - account: string | undefined; - initialCollateralTokenAddress: string | undefined; - toTokenAddress: string | undefined; - initialCollateralDeltaAmount: bigint | undefined; - minOutputAmount: bigint | undefined; - swapPath: string[] | undefined; - executionFee: bigint | undefined; - allowedSlippage: number | undefined; - orderType: OrderType | undefined; -}; - -export type PositionOrderMetricParams = { - account: string | undefined; - referralCodeForTxn: string | undefined; - hasExistingPosition: boolean | undefined; - marketAddress: string | undefined; - initialCollateralTokenAddress: string | undefined; - initialCollateralDeltaAmount: bigint | undefined; - swapPath: string[] | undefined; - sizeDeltaUsd: bigint | undefined; - sizeDeltaInTokens: bigint | undefined; - triggerPrice: bigint | undefined; - acceptablePrice: bigint | undefined; - isLong: boolean | undefined; - orderType: OrderType | undefined; - executionFee: bigint | undefined; -}; - -export type IncreaseOrderMetricParams = { - metricType: "increasePosition" | "limitOrder"; -} & PositionOrderMetricParams; - -export type DecreaseOrderMetricParams = { - metricType: "decreasePosition" | "takeProfitOrder" | "stopLossOrder"; - place: "tradeBox" | "positionSeller"; - isFullClose: boolean | undefined; -} & PositionOrderMetricParams; - -export type EditCollateralMetricParams = { - metricType: "depositCollateral" | "withdrawCollateral"; - account: string | undefined; - marketAddress: string | undefined; - initialCollateralTokenAddress: string | undefined; - initialCollateralDeltaAmount: bigint | undefined; - swapPath: []; - isLong: boolean | undefined; - orderType: OrderType | undefined; - executionFee: bigint | undefined; -}; +import { OrderMetricType } from "./types"; export function getMetricTypeByOrderType(p: { orderType: OrderType; diff --git a/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx b/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx index 92bbb76bbf..6dca1ca45b 100644 --- a/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx +++ b/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -45,12 +45,8 @@ import { WithdrawalStatuses, } from "./types"; import { useMetrics } from "context/MetricsContext/MetricsContext"; -import { - getMetricTypeByOrderType, - getPositionOrderMetricId, - getSwapOrderMetricId, - OrderWsEventMetricData, -} from "context/MetricsContext/utils"; +import { getMetricTypeByOrderType, getPositionOrderMetricId, getSwapOrderMetricId } from "context/MetricsContext/utils"; +import { OrderWsEventMetricData } from "context/MetricsContext/types"; export const SyntheticsEventsContext = createContext({}); From 7bd0a7a8a9413201192ea52c53044c6dd1614234 Mon Sep 17 00:00:00 2001 From: Divhead Date: Tue, 13 Aug 2024 12:52:48 +0300 Subject: [PATCH 15/62] upd texts --- src/locales/de/messages.po | 2 ++ src/locales/en/messages.po | 2 ++ src/locales/es/messages.po | 2 ++ src/locales/fr/messages.po | 2 ++ src/locales/ja/messages.po | 2 ++ src/locales/ko/messages.po | 2 ++ src/locales/pseudo/messages.po | 2 ++ src/locales/ru/messages.po | 2 ++ src/locales/zh/messages.po | 2 ++ 9 files changed, 18 insertions(+) diff --git a/src/locales/de/messages.po b/src/locales/de/messages.po index 691296a3b7..f554f04d61 100644 --- a/src/locales/de/messages.po +++ b/src/locales/de/messages.po @@ -5900,6 +5900,8 @@ msgstr "" msgid "There are insufficient funds in your Subaccount for One-Click Trading. <0>Click here to top-up." msgstr "" +#: src/components/Synthetics/PositionEditor/PositionEditor.tsx +#: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index 8958524c63..7f98346a2f 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -5906,6 +5906,8 @@ msgstr "Transfer {nativeTokenSymbol}" msgid "There are insufficient funds in your Subaccount for One-Click Trading. <0>Click here to top-up." msgstr "There are insufficient funds in your Subaccount for One-Click Trading. <0>Click here to top-up." +#: src/components/Synthetics/PositionEditor/PositionEditor.tsx +#: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx diff --git a/src/locales/es/messages.po b/src/locales/es/messages.po index 1b7232552b..94b5b69a39 100644 --- a/src/locales/es/messages.po +++ b/src/locales/es/messages.po @@ -5900,6 +5900,8 @@ msgstr "" msgid "There are insufficient funds in your Subaccount for One-Click Trading. <0>Click here to top-up." msgstr "" +#: src/components/Synthetics/PositionEditor/PositionEditor.tsx +#: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx diff --git a/src/locales/fr/messages.po b/src/locales/fr/messages.po index c0c55e178e..13caa6d313 100644 --- a/src/locales/fr/messages.po +++ b/src/locales/fr/messages.po @@ -5900,6 +5900,8 @@ msgstr "" msgid "There are insufficient funds in your Subaccount for One-Click Trading. <0>Click here to top-up." msgstr "" +#: src/components/Synthetics/PositionEditor/PositionEditor.tsx +#: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx diff --git a/src/locales/ja/messages.po b/src/locales/ja/messages.po index 13057b0773..070b7521a1 100644 --- a/src/locales/ja/messages.po +++ b/src/locales/ja/messages.po @@ -5900,6 +5900,8 @@ msgstr "" msgid "There are insufficient funds in your Subaccount for One-Click Trading. <0>Click here to top-up." msgstr "" +#: src/components/Synthetics/PositionEditor/PositionEditor.tsx +#: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx diff --git a/src/locales/ko/messages.po b/src/locales/ko/messages.po index ae9709e44f..e06e4c6bda 100644 --- a/src/locales/ko/messages.po +++ b/src/locales/ko/messages.po @@ -5900,6 +5900,8 @@ msgstr "" msgid "There are insufficient funds in your Subaccount for One-Click Trading. <0>Click here to top-up." msgstr "" +#: src/components/Synthetics/PositionEditor/PositionEditor.tsx +#: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx diff --git a/src/locales/pseudo/messages.po b/src/locales/pseudo/messages.po index bca2703ca5..398c7a7bb1 100644 --- a/src/locales/pseudo/messages.po +++ b/src/locales/pseudo/messages.po @@ -5900,6 +5900,8 @@ msgstr "" msgid "There are insufficient funds in your Subaccount for One-Click Trading. <0>Click here to top-up." msgstr "" +#: src/components/Synthetics/PositionEditor/PositionEditor.tsx +#: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx diff --git a/src/locales/ru/messages.po b/src/locales/ru/messages.po index 4e2c0d9dc5..9d52df6d87 100644 --- a/src/locales/ru/messages.po +++ b/src/locales/ru/messages.po @@ -5900,6 +5900,8 @@ msgstr "" msgid "There are insufficient funds in your Subaccount for One-Click Trading. <0>Click here to top-up." msgstr "" +#: src/components/Synthetics/PositionEditor/PositionEditor.tsx +#: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx diff --git a/src/locales/zh/messages.po b/src/locales/zh/messages.po index 4a12f899dc..64760e62b9 100644 --- a/src/locales/zh/messages.po +++ b/src/locales/zh/messages.po @@ -5900,6 +5900,8 @@ msgstr "" msgid "There are insufficient funds in your Subaccount for One-Click Trading. <0>Click here to top-up." msgstr "" +#: src/components/Synthetics/PositionEditor/PositionEditor.tsx +#: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx #: src/components/Synthetics/TradeBox/hooks/useTradeboxTransactions.tsx From 9f7bcb6c811866ff409b041fd972bd380dc34a54 Mon Sep 17 00:00:00 2001 From: Divhead Date: Tue, 13 Aug 2024 13:56:36 +0300 Subject: [PATCH 16/62] useLatest for metrics hook --- src/App/App.tsx | 2 +- .../Synthetics/PositionList/PositionList.tsx | 8 +++-- src/context/MetricsContext/MetricsContext.tsx | 32 ++++++++++++------- src/context/MetricsContext/utils.ts | 8 ++--- .../SyntheticsEventsProvider.tsx | 6 ++-- src/lib/jsonWithBigint.ts | 25 --------------- 6 files changed, 33 insertions(+), 48 deletions(-) delete mode 100644 src/lib/jsonWithBigint.ts diff --git a/src/App/App.tsx b/src/App/App.tsx index 888d942ccb..e0945fb9a2 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -84,7 +84,7 @@ function App() { event: `${metricType}.failed`, isError: true, message: "Pending txn error", - fields: { + data: { ...(metricData || {}), metricType, txnHash: pendingTxn.hash, diff --git a/src/components/Synthetics/PositionList/PositionList.tsx b/src/components/Synthetics/PositionList/PositionList.tsx index 335b809e51..ac2312c900 100644 --- a/src/components/Synthetics/PositionList/PositionList.tsx +++ b/src/components/Synthetics/PositionList/PositionList.tsx @@ -15,6 +15,7 @@ import PositionShare from "components/Exchange/PositionShare"; import { OrderEditorContainer } from "components/OrderEditorContainer/OrderEditorContainer"; import { PositionItem } from "components/Synthetics/PositionItem/PositionItem"; import { useMetrics } from "context/MetricsContext/MetricsContext"; +import { useLatest } from "react-use"; type Props = { onSelectPositionClick: (key: string, tradeMode?: TradeMode) => void; @@ -43,10 +44,11 @@ export function PositionList(p: Props) { const [, setEditingPositionKey] = usePositionEditorPositionState(); const isLoading = useIsPositionsLoading(); + const metricsRef = useLatest(metrics); + useEffect(() => { - metrics.startTimer("positionsList"); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + metricsRef.current.startTimer("positionsList"); + }, [metricsRef]); useEffect(() => { if (positionsInfoData && !isLoaded) { diff --git a/src/context/MetricsContext/MetricsContext.tsx b/src/context/MetricsContext/MetricsContext.tsx index 42cf02bdc6..a3d964060e 100644 --- a/src/context/MetricsContext/MetricsContext.tsx +++ b/src/context/MetricsContext/MetricsContext.tsx @@ -4,7 +4,6 @@ import { useSettings } from "context/SettingsContext/SettingsContextProvider"; import { useSubaccountAddress } from "context/SubaccountContext/SubaccountContext"; import { useOracleKeeperFetcher } from "domain/synthetics/tokens"; import { useChainId } from "lib/chains"; -import { JSONWithBNParse, JSONWwithBNStringify } from "lib/jsonWithBigint"; import { getAppVersion } from "lib/version"; import { getWalletNames } from "lib/wallets/getWalletNames"; import useIsMetamaskMobile from "lib/wallets/useIsMetamaskMobile"; @@ -12,6 +11,7 @@ import mapValues from "lodash/mapValues"; import { Context, PropsWithChildren, useMemo } from "react"; import { createContext, useContextSelector } from "use-context-selector"; import { MetricData, MetricEventType } from "./types"; +import { deserializeBigIntsInObject, serializeBigIntsInObject } from "lib/numbers"; const MAX_METRICS_STORE_TIME = 1000 * 60 * 30; // 30 min @@ -22,7 +22,7 @@ type Timers = { [key: string]: number }; export type MetricsContextType = { sendMetric: (params: { event: MetricEventType; - fields?: MetricData; + data?: MetricData; time?: number; isError: boolean; message?: string; @@ -46,11 +46,11 @@ export function MetricsContextProvider({ children }: PropsWithChildren) { const setCachedMetricData = (metricId: string, metricData: MetricData) => { const cachedMetricsData = localStorage.getItem(CACHED_METRICS_DATA_KEY); - const metricsData: CachedMetricsData = cachedMetricsData ? JSONWithBNParse(cachedMetricsData) : {}; + const metricsData: CachedMetricsData = cachedMetricsData ? deserializeCachedMetricsData(cachedMetricsData) : {}; metricsData[metricId] = { metricId, _metricDataCreated: Date.now(), ...metricData }; - localStorage.setItem(CACHED_METRICS_DATA_KEY, JSONWwithBNStringify(clearOldMetrics(metricsData))); + localStorage.setItem(CACHED_METRICS_DATA_KEY, serializeCachedMetricsData(metricsData)); }; const getCachedMetricData = (metricId: string, clear?: boolean): CachedMetricData | undefined => { @@ -60,13 +60,13 @@ export function MetricsContextProvider({ children }: PropsWithChildren) { return undefined; } - const metricsData = JSONWithBNParse(cachedMetricsData); + const metricsData = deserializeCachedMetricsData(cachedMetricsData); const event = metricsData[metricId]; if (clear) { - metricsData[metricId] = undefined; - localStorage.setItem(CACHED_METRICS_DATA_KEY, JSONWwithBNStringify(clearOldMetrics(metricsData))); + delete metricsData[metricId]; + localStorage.setItem(CACHED_METRICS_DATA_KEY, serializeCachedMetricsData(metricsData)); } return event; @@ -95,7 +95,7 @@ export function MetricsContextProvider({ children }: PropsWithChildren) { } if (clear) { - timers[metricId] = undefined; + delete timers[metricId]; localStorage.setItem(METRICS_TIMERS_KEY, JSON.stringify(clearOldTimers(timers))); } @@ -104,12 +104,12 @@ export function MetricsContextProvider({ children }: PropsWithChildren) { async function sendMetric(params: { event: string; - fields?: any; + data?: MetricData; time?: number; isError: boolean; message?: string; }) { - const { time, isError, fields, message, event } = params; + const { time, isError, data, message, event } = params; const wallets = await getWalletNames(); if (showDebugValues) { @@ -120,7 +120,7 @@ export function MetricsContextProvider({ children }: PropsWithChildren) { wallet: wallets.current, time, isError, - fields, + data, message, }); } @@ -136,7 +136,7 @@ export function MetricsContextProvider({ children }: PropsWithChildren) { isError, time, customFields: { - ...serializeCustomFields(fields), + ...(data ? serializeCustomFields(data) : {}), message, isMobileMetamask, wallets, @@ -180,6 +180,14 @@ function serializeCustomFields(fields: MetricData) { }); } +function serializeCachedMetricsData(metricsData: CachedMetricsData) { + return JSON.stringify(serializeBigIntsInObject(clearOldMetrics(metricsData))); +} + +function deserializeCachedMetricsData(jsonStr: string): CachedMetricsData { + return deserializeBigIntsInObject(JSON.parse(jsonStr)); +} + function clearOldMetrics(metricsData: CachedMetricsData) { const result: { [key: string]: CachedMetricData } = {}; diff --git a/src/context/MetricsContext/utils.ts b/src/context/MetricsContext/utils.ts index 882684bb9e..3af0f17058 100644 --- a/src/context/MetricsContext/utils.ts +++ b/src/context/MetricsContext/utils.ts @@ -92,7 +92,7 @@ export function sendOrderSubmittedMetric(metrics: MetricsContextType, metricId: metrics.sendMetric({ event: `${metricType}.submitted`, isError: false, - fields: metrics.getCachedMetricData(metricId), + data: metrics.getCachedMetricData(metricId), }); } @@ -105,7 +105,7 @@ export function sendTxnValidationErrorMetric( event: `${metricType}.failed`, isError: true, message: "Error submitting order, missed data", - fields: metrics.getCachedMetricData(metricId, true), + data: metrics.getCachedMetricData(metricId, true), }); } @@ -115,7 +115,7 @@ export function getTxnSentMetricsHandler(metrics: MetricsContextType, metricId: event: `${metricType}.sent`, isError: false, time: metrics.getTime(metricId), - fields: metrics.getCachedMetricData(metricId), + data: metrics.getCachedMetricData(metricId), }); return Promise.resolve(); @@ -130,7 +130,7 @@ export function getTxnErrorMetricsHandler(metrics: MetricsContextType, metricId: event: `${metricType}.${isUserRejectedActionError(error as Error) ? "rejected" : "failed"}`, isError: true, message, - fields: metrics.getCachedMetricData(metricId, true), + data: metrics.getCachedMetricData(metricId, true), }); throw error; diff --git a/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx b/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx index 6dca1ca45b..dd55097c30 100644 --- a/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx +++ b/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -115,7 +115,7 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) event: `${metricType}.created`, isError: false, time: metrics.getTime(metricId), - fields: { + data: { ...(metricData || {}), metricType, key: data.key, @@ -174,7 +174,7 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) event: `${metricType}.executed`, isError: false, time: metrics.getTime(metricId, true), - fields: { + data: { ...(metricData || {}), metricType, key, @@ -228,7 +228,7 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) isError: true, message: `Order cancelled`, time: metrics.getTime(metricId, true), - fields: { + data: { ...(metricData || {}), metricType, key, diff --git a/src/lib/jsonWithBigint.ts b/src/lib/jsonWithBigint.ts deleted file mode 100644 index 5e66c4c3aa..0000000000 --- a/src/lib/jsonWithBigint.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const JSONWwithBNStringify = (data) => { - // eslint-disable-next-line no-useless-escape - const bigInts = /([\[:])?"(-?\d+)n"([,\}\]])/g; - const preliminaryJSON = JSON.stringify(data, (_, value) => - typeof value === "bigint" ? value.toString() + "n" : value - ); - const finalJSON = preliminaryJSON.replace(bigInts, "$1$2$3"); - - return finalJSON; -}; - -export const JSONWithBNParse = (json) => { - const numbersBiggerThanMaxInt = - // eslint-disable-next-line no-useless-escape - /(?<=[^\\]":[\[]?|[^\\]":\[.*[^\.\d*])(-?\d{17,}|-?(?:[9](?:[1-9]07199254740991|0[1-9]7199254740991|00[8-9]199254740991|007[2-9]99254740991|007199[3-9]54740991|0071992[6-9]4740991|00719925[5-9]740991|007199254[8-9]40991|0071992547[5-9]0991|00719925474[1-9]991|00719925474099[2-9])))(?=,|\}[^"]|\][^"])/g; - const serializedData = json.replace(numbersBiggerThanMaxInt, `"$1n"`); - - return JSON.parse(serializedData, (_, value) => { - const isCustomFormatBigInt = typeof value === "string" && Boolean(value.match(/^-?\d+n$/)); - - if (isCustomFormatBigInt) return BigInt(value.substring(0, value.length - 1)); - - return value; - }); -}; From 8415364dc3945f5b93bf24fbcbd5745025d32eb8 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Thu, 1 Aug 2024 15:22:49 +0000 Subject: [PATCH 17/62] Refacor --- .../Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx | 1013 ++++++----------- .../GmSwap/GmSwapBox/HighPriceImpactRow.tsx | 40 + .../Synthetics/GmSwap/GmSwapBox/InfoRows.tsx | 94 ++ .../Synthetics/GmSwap/GmSwapBox/Swap.tsx | 11 + .../GmSwapBox/getGmSwapBoxAvailableModes.tsx | 2 +- .../GmSwap/GmSwapBox/showMarketToast.tsx | 20 + .../GmSwapBox/useDepositWithdrawalAmounts.tsx | 116 ++ .../GmSwap/GmSwapBox/useGmSwapBoxState.tsx | 62 + .../GmSwapBox/useUpdateByQueryParams.tsx | 128 +++ .../GmSwapBox/useUpdateInputAmounts.tsx | 177 +++ .../GmSwap/GmSwapBox/useUpdateTokens.tsx | 94 ++ src/domain/synthetics/trade/types.ts | 2 +- .../synthetics/trade/utils/withdrawal.ts | 4 +- src/pages/MarketPoolsPage/MarketPoolsPage.tsx | 10 +- 14 files changed, 1105 insertions(+), 668 deletions(-) create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/InfoRows.tsx create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/Swap.tsx create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/showMarketToast.tsx create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/useDepositWithdrawalAmounts.tsx create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/useGmSwapBoxState.tsx create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/useUpdateByQueryParams.tsx create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/useUpdateInputAmounts.tsx create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/useUpdateTokens.tsx diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx index 92aecac877..4cd09d5048 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx @@ -1,21 +1,18 @@ -import { msg, t, Trans } from "@lingui/macro"; +import { msg, t } from "@lingui/macro"; import { useConnectModal } from "@rainbow-me/rainbowkit"; import cx from "classnames"; -import { isAddress } from "ethers"; -import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react"; -import { IoMdSwap } from "react-icons/io"; -import { useHistory } from "react-router-dom"; +import { Dispatch, SetStateAction, useCallback, useEffect, useMemo } from "react"; import { HIGH_PRICE_IMPACT_BPS } from "config/factors"; -import { getSyntheticsDepositIndexTokenKey, SYNTHETICS_MARKET_DEPOSIT_TOKEN_KEY } from "config/localStorage"; -import { convertTokenAddress, getTokenBySymbolSafe, NATIVE_TOKEN_ADDRESS } from "config/tokens"; +import { NATIVE_TOKEN_ADDRESS } from "config/tokens"; import { MAX_METAMASK_MOBILE_DECIMALS } from "config/ui"; import { useSettings } from "context/SettingsContext/SettingsContextProvider"; +import { useMarketsInfoData, useTokensData } from "context/SyntheticsStateContext/hooks/globalsHooks"; import { useHasOutdatedUi } from "domain/legacy"; import { + FeeItem, estimateExecuteDepositGasLimit, estimateExecuteWithdrawalGasLimit, - FeeItem, getExecutionFee, getFeeItem, getTotalFeeItem, @@ -28,62 +25,50 @@ import { } from "domain/synthetics/fees/utils/estimateOraclePriceCount"; import useUiFeeFactor from "domain/synthetics/fees/utils/useUiFeeFactor"; import { useMarketTokensData } from "domain/synthetics/markets"; -import { Market, MarketsInfoData } from "domain/synthetics/markets/types"; +import { MarketInfo } from "domain/synthetics/markets/types"; import { getAvailableUsdLiquidityForCollateral, getMarketIndexName, - getMarketPoolName, getTokenPoolType, } from "domain/synthetics/markets/utils"; -import { convertToUsd, getTokenData, TokenData, TokensData } from "domain/synthetics/tokens"; +import { TokenData, convertToUsd, getTokenData } from "domain/synthetics/tokens"; import { useGmTokensFavorites } from "domain/synthetics/tokens/useGmTokensFavorites"; import { GmSwapFees, useAvailableTokenOptions } from "domain/synthetics/trade"; import useSortedPoolsWithIndexToken from "domain/synthetics/trade/useSortedPoolsWithIndexToken"; -import { getDepositAmounts } from "domain/synthetics/trade/utils/deposit"; import { getCommonError, getGmSwapError } from "domain/synthetics/trade/utils/validation"; -import { getWithdrawalAmounts } from "domain/synthetics/trade/utils/withdrawal"; -import { getMinResidualAmount, Token } from "domain/tokens"; +import { Token, getMinResidualAmount } from "domain/tokens"; import { bigMath } from "lib/bigmath"; import { useChainId } from "lib/chains"; -import { helperToast } from "lib/helperToast"; import { useLocalizedMap } from "lib/i18n"; -import { useLocalStorageSerializeKey } from "lib/localStorage"; -import { BN_ZERO, formatAmountFree, formatTokenAmount, formatUsd, limitDecimals, parseValue } from "lib/numbers"; -import { getByKey, getMatchingValueFromObject } from "lib/objects"; -import { useSafeState } from "lib/useSafeState"; -import useSearchParams from "lib/useSearchParams"; +import { formatAmountFree, formatTokenAmount, formatUsd, limitDecimals, parseValue } from "lib/numbers"; +import { getByKey } from "lib/objects"; import useIsMetamaskMobile from "lib/wallets/useIsMetamaskMobile"; import useWallet from "lib/wallets/useWallet"; + import { getGmSwapBoxAvailableModes } from "./getGmSwapBoxAvailableModes"; +import { showMarketToast } from "./showMarketToast"; +import { useDepositWithdrawalAmounts } from "./useDepositWithdrawalAmounts"; +import { useGmSwapBoxState } from "./useGmSwapBoxState"; +import { useUpdateByQueryParams } from "./useUpdateByQueryParams"; +import { useUpdateInputAmounts } from "./useUpdateInputAmounts"; +import { useUpdateTokens } from "./useUpdateTokens"; import Button from "components/Button/Button"; import BuyInputSection from "components/BuyInputSection/BuyInputSection"; -import Checkbox from "components/Checkbox/Checkbox"; -import { ExchangeInfo } from "components/Exchange/ExchangeInfo"; -import ExchangeInfoRow from "components/Exchange/ExchangeInfoRow"; import { PoolSelector } from "components/MarketSelector/PoolSelector"; -import { GmFees } from "components/Synthetics/GmSwap/GmFees/GmFees"; -import { NetworkFeeRow } from "components/Synthetics/NetworkFeeRow/NetworkFeeRow"; import Tab from "components/Tab/Tab"; import TokenWithIcon from "components/TokenIcon/TokenWithIcon"; import TokenSelector from "components/TokenSelector/TokenSelector"; -import Tooltip from "components/Tooltip/Tooltip"; import { GmConfirmationBox } from "../GmConfirmationBox/GmConfirmationBox"; +import { InfoRows } from "./InfoRows"; +import { Swap } from "./Swap"; import "./GmSwapBox.scss"; -type SearchParams = { - market?: string; - operation?: string; - mode?: string; - from?: string; - pool?: string; - scroll?: string; -}; - export enum Operation { Deposit = "Deposit", Withdrawal = "Withdrawal", + Shift = "Shift", } export enum Mode { @@ -93,14 +78,11 @@ export enum Mode { type Props = { selectedMarketAddress?: string; - markets: Market[]; - marketsInfoData?: MarketsInfoData; - tokensData?: TokensData; onSelectMarket: (marketAddress: string) => void; operation: Operation; mode: Mode; - setMode: Dispatch>; - setOperation: Dispatch>; + onSetMode: Dispatch>; + onSetOperation: Dispatch>; }; const OPERATION_LABELS = { @@ -114,65 +96,83 @@ const MODE_LABELS = { }; export function GmSwapBox(p: Props) { - const { operation, mode, setMode, setOperation, onSelectMarket, marketsInfoData, tokensData } = p; + const { selectedMarketAddress, operation, mode, onSetMode, onSetOperation, onSelectMarket } = p; const isMetamaskMobile = useIsMetamaskMobile(); - const history = useHistory(); const { openConnectModal } = useConnectModal(); - const searchParams = useSearchParams(); - const marketAddress = p.selectedMarketAddress; + + const marketAddress = selectedMarketAddress; const { shouldDisableValidationForTesting } = useSettings(); const { chainId } = useChainId(); const { account } = useWallet(); - const gmTokenFavoritesContext = useGmTokensFavorites(); - - const nativeToken = getByKey(tokensData, NATIVE_TOKEN_ADDRESS); - const minResidualAmount = getMinResidualAmount(nativeToken?.decimals, nativeToken?.prices?.maxPrice); - - const uiFeeFactor = useUiFeeFactor(chainId); - + // #region Requests + const { marketTokensData: depositMarketTokensData } = useMarketTokensData(chainId, { isDeposit: true }); + const { marketTokensData: withdrawalMarketTokensData } = useMarketTokensData(chainId, { isDeposit: false }); const gasLimits = useGasLimits(chainId); const gasPrice = useGasPrice(chainId); - + const uiFeeFactor = useUiFeeFactor(chainId); const { data: hasOutdatedUi } = useHasOutdatedUi(); - const { marketTokensData: depositMarketTokensData } = useMarketTokensData(chainId, { isDeposit: true }); - const { marketTokensData: withdrawalMarketTokensData } = useMarketTokensData(chainId, { isDeposit: false }); + // #endregion - const [focusedInput, setFocusedInput] = useState<"longCollateral" | "shortCollateral" | "market">("market"); - const [stage, setStage] = useState<"swap" | "confirmation" | "processing">(); - const [isHighPriceImpactAccepted, setIsHighPriceImpactAccepted] = useState(false); + // #region Selectors + const marketsInfoData = useMarketsInfoData(); const { marketsInfo: sortedMarketsInfoByIndexToken } = useSortedPoolsWithIndexToken( marketsInfoData, depositMarketTokensData ); + const tokensData = useTokensData(); + const { infoTokens } = useAvailableTokenOptions(chainId, { marketsInfoData, tokensData }); + // #endregion + + // #region State + const gmTokenFavoritesContext = useGmTokensFavorites(); + const { + focusedInput, + setFocusedInput, + + stage, + setStage, + + isHighPriceImpactAccepted, + setIsHighPriceImpactAccepted, + + indexName, + setIndexName, + + firstTokenAddress, + setFirstTokenAddress, + + secondTokenAddress, + setSecondTokenAddress, + + firstTokenInputValue, + setFirstTokenInputValue, + + secondTokenInputValue, + setSecondTokenInputValue, + + marketTokenInputValue, + setMarketTokenInputValue, + } = useGmSwapBoxState(operation, mode, marketAddress); + // #endregion + + // #region Derived state + const nativeToken = getByKey(tokensData, NATIVE_TOKEN_ADDRESS); + const minResidualAmount = getMinResidualAmount(nativeToken?.decimals, nativeToken?.prices?.maxPrice); const isDeposit = operation === Operation.Deposit; const isWithdrawal = operation === Operation.Withdrawal; + const isShift = operation === Operation.Shift; const isSingle = mode === Mode.Single; const isPair = mode === Mode.Pair; const marketTokensData = isDeposit ? depositMarketTokensData : withdrawalMarketTokensData; - const markets = useMemo( - () => Object.values(marketsInfoData || {}).filter((marketInfo) => !marketInfo.isDisabled), - [marketsInfoData] - ); + const marketInfo = getByKey(marketsInfoData, marketAddress); const availableModes = getGmSwapBoxAvailableModes(operation, marketInfo); - const [indexName, setIndexName] = useLocalStorageSerializeKey( - getSyntheticsDepositIndexTokenKey(chainId), - undefined - ); - const { infoTokens } = useAvailableTokenOptions(chainId, { marketsInfoData, tokensData }); - - const [firstTokenAddress, setFirstTokenAddress] = useLocalStorageSerializeKey( - [chainId, SYNTHETICS_MARKET_DEPOSIT_TOKEN_KEY, isDeposit, marketAddress, "first"], - undefined - ); - const firstToken = getTokenData(tokensData, firstTokenAddress); - const [firstTokenInputValue, setFirstTokenInputValue] = useSafeState(""); let firstTokenAmount = parseValue(firstTokenInputValue, firstToken?.decimals || 0); const firstTokenUsd = convertToUsd( firstTokenAmount, @@ -180,14 +180,8 @@ export function GmSwapBox(p: Props) { isDeposit ? firstToken?.prices?.minPrice : firstToken?.prices?.maxPrice ); - const [secondTokenAddress, setSecondTokenAddress] = useLocalStorageSerializeKey( - [chainId, SYNTHETICS_MARKET_DEPOSIT_TOKEN_KEY, isDeposit, marketAddress, "second"], - undefined - ); const secondToken = getTokenData(tokensData, secondTokenAddress); - const [secondTokenInputValue, setSecondTokenInputValue] = useSafeState(""); let secondTokenAmount = parseValue(secondTokenInputValue, secondToken?.decimals || 0); - const secondTokenUsd = convertToUsd( secondTokenAmount, secondToken?.decimals, @@ -258,27 +252,29 @@ export function GmSwapBox(p: Props) { setSecondTokenInputValue, ]); - const tokenOptions: Token[] = (function getTokenOptions() { - const { longToken, shortToken } = marketInfo || {}; + const tokenOptions: Token[] = useMemo( + function getTokenOptions() { + const { longToken, shortToken } = marketInfo || {}; - if (!longToken || !shortToken) return []; + if (!longToken || !shortToken) return []; - const result = [longToken]; + const result = [longToken]; - if (longToken.address !== shortToken.address) { - result.push(shortToken); - } + if (longToken.address !== shortToken.address) { + result.push(shortToken); + } - const nativeToken = getByKey(tokensData, NATIVE_TOKEN_ADDRESS)!; + const nativeToken = getByKey(tokensData, NATIVE_TOKEN_ADDRESS)!; - if (result.some((token) => token.isWrapped) && nativeToken) { - result.unshift(nativeToken); - } + if (result.some((token) => token.isWrapped) && nativeToken) { + result.unshift(nativeToken); + } - return result; - })(); + return result; + }, + [marketInfo, tokensData] + ); - const [marketTokenInputValue, setMarketTokenInputValue] = useSafeState(); const marketToken = getTokenData( isDeposit ? depositMarketTokensData : withdrawalMarketTokensData, marketInfo?.marketTokenAddress @@ -299,93 +295,17 @@ export function GmSwapBox(p: Props) { }; }, [marketInfo]); - const halfOfLong = longTokenInputState?.amount !== undefined ? longTokenInputState.amount / 2n : undefined; - - const depositAmounts = useMemo(() => { - if (!isDeposit || !marketInfo || !marketToken) { - return undefined; - } - - const longTokenAmount = (marketInfo.isSameCollaterals ? halfOfLong : longTokenInputState?.amount) || BN_ZERO; - const shortTokenAmount = - (marketInfo.isSameCollaterals - ? longTokenInputState?.amount !== undefined - ? longTokenInputState.amount - longTokenAmount - : undefined - : shortTokenInputState?.amount) || BN_ZERO; - - return getDepositAmounts({ - marketInfo, - marketToken, - longToken: marketInfo.longToken, - shortToken: marketInfo.shortToken, - longTokenAmount, - shortTokenAmount, - marketTokenAmount, - includeLongToken: Boolean(longTokenInputState?.address), - includeShortToken: Boolean(shortTokenInputState?.address), - uiFeeFactor, - strategy: focusedInput === "market" ? "byMarketToken" : "byCollaterals", - }); - }, [ - focusedInput, - halfOfLong, + const amounts = useDepositWithdrawalAmounts({ isDeposit, - longTokenInputState?.address, - longTokenInputState?.amount, marketInfo, marketToken, + longTokenInputState, + shortTokenInputState, marketTokenAmount, - shortTokenInputState?.address, - shortTokenInputState?.amount, uiFeeFactor, - ]); - - const withdrawalAmounts = useMemo(() => { - if (!isWithdrawal || !marketInfo || !marketToken) { - return undefined; - } - - let strategy; - if (focusedInput === "market") { - strategy = "byMarketToken"; - } else if (focusedInput === "longCollateral") { - strategy = "byLongCollateral"; - } else { - strategy = "byShortCollateral"; - } - - const longTokenAmount = marketInfo.isSameCollaterals - ? halfOfLong ?? BN_ZERO - : longTokenInputState?.amount ?? BN_ZERO; - const shortTokenAmount = marketInfo.isSameCollaterals - ? longTokenInputState?.amount !== undefined - ? longTokenInputState?.amount - longTokenAmount - : undefined ?? BN_ZERO - : shortTokenInputState?.amount ?? BN_ZERO; - - return getWithdrawalAmounts({ - marketInfo, - marketToken, - marketTokenAmount, - longTokenAmount, - shortTokenAmount, - strategy, - uiFeeFactor, - }); - }, [ focusedInput, - halfOfLong, isWithdrawal, - longTokenInputState?.amount, - marketInfo, - marketToken, - marketTokenAmount, - shortTokenInputState?.amount, - uiFeeFactor, - ]); - - const amounts = isDeposit ? depositAmounts : withdrawalAmounts; + }); const { fees, executionFee } = useMemo(() => { if (!gasLimits || gasPrice === undefined || !tokensData || !amounts) { @@ -485,45 +405,77 @@ export function GmSwapBox(p: Props) { onSubmit, }; }, [ - account, - amounts?.longTokenAmount, - amounts?.longTokenUsd, - amounts?.marketTokenUsd, - amounts?.shortTokenAmount, - amounts?.shortTokenUsd, chainId, - fees, hasOutdatedUi, isDeposit, - isHighPriceImpact, - isHighPriceImpactAccepted, - longCollateralLiquidityUsd, marketInfo, marketToken, + longTokenInputState?.token, + shortTokenInputState?.token, marketTokenAmount, - openConnectModal, + amounts?.marketTokenUsd, + amounts?.longTokenAmount, + amounts?.shortTokenAmount, + amounts?.longTokenUsd, + amounts?.shortTokenUsd, + longCollateralLiquidityUsd, shortCollateralLiquidityUsd, + fees, + isHighPriceImpact, + isHighPriceImpactAccepted, + account, + openConnectModal, + setStage, shouldDisableValidationForTesting, - longTokenInputState?.token, - shortTokenInputState?.token, ]); - function onFocusedCollateralInputChange(tokenAddress: string) { - if (!marketInfo) { - return; - } + const firstTokenShowMaxButton = + (isDeposit && + firstToken?.balance && + (firstTokenAmount === undefined || firstTokenAmount !== firstToken.balance) && + (firstToken?.isNative + ? minResidualAmount !== undefined && firstToken?.balance !== undefined && firstToken.balance > minResidualAmount + : true)) || + false; + + const secondTokenShowMaxButton = + (isDeposit && + secondToken?.balance && + (secondTokenAmount === undefined || secondTokenAmount !== secondToken.balance) && + (secondToken?.isNative + ? minResidualAmount !== undefined && + secondToken?.balance !== undefined && + secondToken.balance > minResidualAmount + : true)) || + false; + + const marketTokenInputShowMaxButton = + (isWithdrawal && + marketToken?.balance && + (marketTokenAmount === undefined || marketTokenAmount !== marketToken.balance)) || + false; + // #endregion + + // #region Callbacks + const onFocusedCollateralInputChange = useCallback( + (tokenAddress: string) => { + if (!marketInfo) { + return; + } - if (marketInfo.isSameCollaterals) { - setFocusedInput("longCollateral"); - return; - } + if (marketInfo.isSameCollaterals) { + setFocusedInput("longCollateral"); + return; + } - if (getTokenPoolType(marketInfo, tokenAddress) === "long") { - setFocusedInput("longCollateral"); - } else { - setFocusedInput("shortCollateral"); - } - } + if (getTokenPoolType(marketInfo, tokenAddress) === "long") { + setFocusedInput("longCollateral"); + } else { + setFocusedInput("shortCollateral"); + } + }, + [marketInfo, setFocusedInput] + ); const resetInputs = useCallback(() => { setFirstTokenInputValue(""); @@ -534,15 +486,15 @@ export function GmSwapBox(p: Props) { const onSwitchSide = useCallback(() => { setFocusedInput("market"); resetInputs(); - setOperation(operation === Operation.Deposit ? Operation.Withdrawal : Operation.Deposit); - }, [operation, resetInputs, setOperation]); + onSetOperation(operation === Operation.Deposit ? Operation.Withdrawal : Operation.Deposit); + }, [operation, resetInputs, setFocusedInput, onSetOperation]); const onOperationChange = useCallback( (operation: Operation) => { resetInputs(); - setOperation(operation); + onSetOperation(operation); }, - [resetInputs, setOperation] + [resetInputs, onSetOperation] ); const onMarketChange = useCallback( @@ -553,330 +505,214 @@ export function GmSwapBox(p: Props) { [onSelectMarket, resetInputs] ); - useEffect( - function updateInputAmounts() { - if (!marketToken || !marketInfo) { - return; + const onMaxClickFirstToken = useCallback(() => { + if (firstToken?.balance) { + let maxAvailableAmount = firstToken.isNative + ? firstToken.balance - (minResidualAmount ?? 0n) + : firstToken.balance; + + if (maxAvailableAmount < 0) { + maxAvailableAmount = 0n; } - const longToken = longTokenInputState?.token; - const shortToken = shortTokenInputState?.token; - - if (isDeposit) { - if (["longCollateral", "shortCollateral"].includes(focusedInput)) { - if ((amounts?.longTokenUsd ?? 0) <= 0 && (amounts?.shortTokenUsd ?? 0) <= 0) { - setMarketTokenInputValue(""); - return; - } - - if (amounts) { - setMarketTokenInputValue( - amounts.marketTokenAmount > 0 ? formatAmountFree(amounts.marketTokenAmount, marketToken.decimals) : "" - ); - } - } else if (focusedInput === "market") { - if (marketTokenAmount <= 0) { - longTokenInputState?.setValue(""); - shortTokenInputState?.setValue(""); - return; - } - - if (amounts) { - if (longToken) { - longTokenInputState?.setValue( - amounts.longTokenAmount > 0 ? formatAmountFree(amounts.longTokenAmount, longToken.decimals) : "" - ); - } - if (shortToken) { - shortTokenInputState?.setValue( - amounts.shortTokenAmount > 0 ? formatAmountFree(amounts.shortTokenAmount, shortToken.decimals) : "" - ); - } - return; - } - } + const formattedMaxAvailableAmount = formatAmountFree(maxAvailableAmount, firstToken.decimals); + const finalAmount = isMetamaskMobile + ? limitDecimals(formattedMaxAvailableAmount, MAX_METAMASK_MOBILE_DECIMALS) + : formattedMaxAvailableAmount; - return; - } + setFirstTokenInputValue(finalAmount); + onFocusedCollateralInputChange(firstToken.address); + } + }, [ + firstToken?.address, + firstToken?.balance, + firstToken?.decimals, + firstToken?.isNative, + isMetamaskMobile, + minResidualAmount, + onFocusedCollateralInputChange, + setFirstTokenInputValue, + ]); + + const onMaxClickSecondToken = useCallback(() => { + if (!isDeposit) { + return; + } + + if (secondToken?.balance === undefined) { + return; + } + + let maxAvailableAmount = secondToken.isNative + ? secondToken.balance - (minResidualAmount ?? 0n) + : secondToken.balance; - if (isWithdrawal) { - if (focusedInput === "market") { - if ((amounts?.marketTokenAmount ?? 0) <= 0) { - longTokenInputState?.setValue(""); - shortTokenInputState?.setValue(""); - return; - } - - if (amounts) { - if (marketInfo.isSameCollaterals) { - if (longToken) { - setFirstTokenInputValue( - amounts.longTokenAmount > 0 - ? formatAmountFree(amounts.longTokenAmount + amounts.shortTokenAmount, longToken.decimals) - : "" - ); - } - } else { - if (longToken) { - longTokenInputState?.setValue( - amounts.longTokenAmount > 0 ? formatAmountFree(amounts.longTokenAmount, longToken.decimals) : "" - ); - } - if (shortToken) { - shortTokenInputState?.setValue( - amounts.shortTokenAmount > 0 ? formatAmountFree(amounts.shortTokenAmount, shortToken.decimals) : "" - ); - } - } - } - } else if (["longCollateral", "shortCollateral"].includes(focusedInput)) { - if (focusedInput === "longCollateral" && (amounts?.longTokenAmount ?? 0) <= 0) { - shortTokenInputState?.setValue(""); - setMarketTokenInputValue(""); - return; - } - - if (focusedInput === "shortCollateral" && (amounts?.shortTokenAmount ?? 0) <= 0) { - longTokenInputState?.setValue(""); - setMarketTokenInputValue(""); - return; - } - - if (amounts) { - setMarketTokenInputValue( - amounts.marketTokenAmount > 0 ? formatAmountFree(amounts.marketTokenAmount, marketToken.decimals) : "" - ); - if (marketInfo.isSameCollaterals) { - if (longToken) { - longTokenInputState?.setValue( - formatAmountFree(amounts.longTokenAmount + amounts.shortTokenAmount, longToken.decimals) - ); - } - } else { - if (longToken) { - longTokenInputState?.setValue(formatAmountFree(amounts.longTokenAmount, longToken.decimals)); - } - if (shortToken) { - shortTokenInputState?.setValue(formatAmountFree(amounts.shortTokenAmount, shortToken.decimals)); - } - } - } - } + if (maxAvailableAmount < 0) { + maxAvailableAmount = 0n; + } + + const formattedMaxAvailableAmount = formatAmountFree(maxAvailableAmount, secondToken.decimals); + const finalAmount = isMetamaskMobile + ? limitDecimals(formattedMaxAvailableAmount, MAX_METAMASK_MOBILE_DECIMALS) + : formattedMaxAvailableAmount; + setSecondTokenInputValue(finalAmount); + onFocusedCollateralInputChange(secondToken.address); + }, [ + isDeposit, + isMetamaskMobile, + minResidualAmount, + onFocusedCollateralInputChange, + secondToken?.address, + secondToken?.balance, + secondToken?.decimals, + secondToken?.isNative, + setSecondTokenInputValue, + ]); + + const localizedOperationLabels = useLocalizedMap(OPERATION_LABELS); + const localizedModeLabels = useLocalizedMap(MODE_LABELS); + + const handleFirstTokenInputValueChange = useCallback( + (e) => { + if (firstToken) { + setFirstTokenInputValue(e.target.value); + onFocusedCollateralInputChange(firstToken.address); } }, - [ - amounts, - focusedInput, - isDeposit, - isWithdrawal, - longTokenInputState, - marketInfo, - marketToken, - marketTokenAmount, - setFirstTokenInputValue, - setMarketTokenInputValue, - setSecondTokenInputValue, - shortTokenInputState, - ] + [firstToken, onFocusedCollateralInputChange, setFirstTokenInputValue] ); - useEffect( - function updateIndexToken() { - if (!indexName && markets.length) { - setIndexName(getMarketIndexName(markets[0])); - } + const handleFormSubmit = useCallback( + (e: React.FormEvent) => { + e.preventDefault(); + submitState.onSubmit(); }, - [indexName, markets, setIndexName] + [submitState] ); - useEffect( - function updateMarket() { - const marketsByIndexName = markets.filter((market) => getMarketIndexName(market) === indexName); + const marketTokenInputClickMax = useCallback(() => { + if (marketToken?.balance) { + const formattedGMBalance = formatAmountFree(marketToken.balance, marketToken.decimals); + const finalGMBalance = isMetamaskMobile + ? limitDecimals(formattedGMBalance, MAX_METAMASK_MOBILE_DECIMALS) + : formattedGMBalance; + setMarketTokenInputValue(finalGMBalance); + setFocusedInput("market"); + } + }, [isMetamaskMobile, marketToken?.balance, marketToken?.decimals, setFocusedInput, setMarketTokenInputValue]); - if (!marketsByIndexName.length) { - return; - } + const marketTokenInputClickTopRightLabel = useCallback(() => { + if (!isWithdrawal) { + return; + } - if (!marketAddress || !marketsByIndexName.find((market) => market.marketTokenAddress === marketAddress)) { - onMarketChange(marketsByIndexName[0].marketTokenAddress); - } + if (marketToken?.balance) { + setMarketTokenInputValue(formatAmountFree(marketToken.balance, marketToken.decimals)); + setFocusedInput("market"); + } + }, [isWithdrawal, marketToken?.balance, marketToken?.decimals, setFocusedInput, setMarketTokenInputValue]); + + const marketTokenInputValueChange = useCallback( + (e: React.ChangeEvent) => { + setMarketTokenInputValue(e.target.value); + setFocusedInput("market"); }, - [indexName, marketAddress, markets, onMarketChange] + [setFocusedInput, setMarketTokenInputValue] ); - useEffect( - function updateByQueryParams() { - const { market: marketRaw, operation, mode, from: fromToken, pool, scroll } = searchParams; - const marketAddress = marketRaw?.toLowerCase(); - - if (operation) { - let finalOperation; - - if (operation.toLowerCase() === "buy") { - finalOperation = Operation.Deposit; - } else if (operation.toLowerCase() === "sell") { - finalOperation = Operation.Withdrawal; - } - - if (finalOperation) { - setOperation(finalOperation as Operation); - } - } - - if (mode) { - const validMode = getMatchingValueFromObject(Mode, mode); - if (validMode) { - setMode(validMode as Mode); - } + const secondTokenInputValueChange = useCallback( + (e: React.ChangeEvent) => { + if (secondToken) { + setSecondTokenInputValue(e.target.value); + onFocusedCollateralInputChange(secondToken.address); } + }, + [onFocusedCollateralInputChange, secondToken, setSecondTokenInputValue] + ); - if (fromToken) { - const fromTokenInfo = getTokenBySymbolSafe(chainId, fromToken, { - version: "v2", - }); - if (fromTokenInfo) { - setFirstTokenAddress(convertTokenAddress(chainId, fromTokenInfo.address, "wrapped")); - } - } + const firstTokenSelectToken = useCallback( + (token: Token): void => setFirstTokenAddress(token.address), + [setFirstTokenAddress] + ); - if (scroll === "1") { - window.scrollTo({ top: 0, left: 0 }); - } + const marketTokenSelectMarket = useCallback( + (marketInfo: MarketInfo): void => { + setIndexName(getMarketIndexName(marketInfo)); + onMarketChange(marketInfo.marketTokenAddress); + showMarketToast(marketInfo); + }, + [onMarketChange, setIndexName] + ); + // #endregion - if ((marketAddress || pool) && markets.length > 0) { - if (marketAddress && isAddress(marketAddress)) { - const marketInfo = markets.find((market) => market.marketTokenAddress.toLowerCase() === marketAddress); - if (marketInfo) { - setIndexName(getMarketIndexName(marketInfo)); - onSelectMarket(marketInfo.marketTokenAddress); - const indexName = getMarketIndexName(marketInfo); - const poolName = getMarketPoolName(marketInfo); - helperToast.success( - -
- GM: {indexName} - [{poolName}] -
{" "} - selected in order form -
- ); - } - } - - if (history.location.search) { - history.replace({ search: "" }); - } - } + // #region Effects + useUpdateInputAmounts({ + marketToken, + marketInfo, + longTokenInputState, + shortTokenInputState, + isDeposit, + focusedInput, + amounts, + setMarketTokenInputValue, + marketTokenAmount, + isWithdrawal, + setFirstTokenInputValue, + setSecondTokenInputValue, + }); - if (!marketAddress && !pool) { - if (history.location.search) { - history.replace({ search: "" }); - } + useEffect( + function updateIndexToken() { + if (!indexName && sortedMarketsInfoByIndexToken.length) { + setIndexName(getMarketIndexName(sortedMarketsInfoByIndexToken[0])); } }, - [history, onSelectMarket, searchParams, setIndexName, setOperation, setMode, setFirstTokenAddress, chainId, markets] + [indexName, sortedMarketsInfoByIndexToken, setIndexName] ); useEffect( - function updateTokens() { - if (!tokenOptions.length) return; - - if (!tokenOptions.find((token) => token.address === firstTokenAddress)) { - setFirstTokenAddress(tokenOptions[0].address); - } + function updateMarket() { + const marketsByIndexName = sortedMarketsInfoByIndexToken.filter( + (market) => getMarketIndexName(market) === indexName + ); - if (isSingle && secondTokenAddress && marketInfo && secondTokenAmount !== undefined && secondTokenAmount > 0) { - const secondTokenPoolType = getTokenPoolType(marketInfo, secondTokenAddress); - setFocusedInput(secondTokenPoolType === "long" ? "longCollateral" : "shortCollateral"); - setSecondTokenAddress(undefined); - setSecondTokenInputValue(""); + if (!marketsByIndexName.length) { return; } - if (isPair && firstTokenAddress) { - if (marketInfo?.isSameCollaterals) { - if (!secondTokenAddress || firstTokenAddress !== secondTokenAddress) { - setSecondTokenAddress(firstTokenAddress); - } - - return; - } - - if ( - !secondTokenAddress || - !tokenOptions.find((token) => token.address === secondTokenAddress) || - convertTokenAddress(chainId, firstTokenAddress, "wrapped") === - convertTokenAddress(chainId, secondTokenAddress, "wrapped") - ) { - const secondToken = tokenOptions.find((token) => { - return ( - convertTokenAddress(chainId, token.address, "wrapped") !== - convertTokenAddress(chainId, firstTokenAddress, "wrapped") - ); - }); - setSecondTokenAddress(secondToken?.address); - } + if (!marketAddress || !marketsByIndexName.find((market) => market.marketTokenAddress === marketAddress)) { + onMarketChange(marketsByIndexName[0].marketTokenAddress); } }, - [ - chainId, - firstTokenAddress, - isPair, - isSingle, - marketInfo, - secondTokenAddress, - secondTokenAmount, - setFirstTokenAddress, - setSecondTokenAddress, - setSecondTokenInputValue, - tokenOptions, - ] + [indexName, marketAddress, sortedMarketsInfoByIndexToken, onMarketChange] ); - function onMaxClickFirstToken() { - if (firstToken?.balance) { - let maxAvailableAmount = firstToken.isNative - ? firstToken.balance - (minResidualAmount ?? 0n) - : firstToken.balance; - - if (maxAvailableAmount < 0) { - maxAvailableAmount = 0n; - } - - const formattedMaxAvailableAmount = formatAmountFree(maxAvailableAmount, firstToken.decimals); - const finalAmount = isMetamaskMobile - ? limitDecimals(formattedMaxAvailableAmount, MAX_METAMASK_MOBILE_DECIMALS) - : formattedMaxAvailableAmount; - - setFirstTokenInputValue(finalAmount); - onFocusedCollateralInputChange(firstToken.address); - } - } + useUpdateByQueryParams({ + setOperation: onSetOperation, + setMode: onSetMode, + setIndexName, + onSelectMarket, + setFirstTokenAddress, + }); - function onMaxClickSecondToken() { - if (secondToken?.balance) { - let maxAvailableAmount = secondToken.isNative - ? secondToken.balance - (minResidualAmount ?? 0n) - : secondToken.balance; - - if (maxAvailableAmount < 0) { - maxAvailableAmount = 0n; - } - - const formattedMaxAvailableAmount = formatAmountFree(maxAvailableAmount, secondToken.decimals); - const finalAmount = isMetamaskMobile - ? limitDecimals(formattedMaxAvailableAmount, MAX_METAMASK_MOBILE_DECIMALS) - : formattedMaxAvailableAmount; - setSecondTokenInputValue(finalAmount); - onFocusedCollateralInputChange(secondToken.address); - } - } - - const localizedOperationLabels = useLocalizedMap(OPERATION_LABELS); - const localizedModeLabels = useLocalizedMap(MODE_LABELS); + useUpdateTokens({ + tokenOptions, + firstTokenAddress, + setFirstTokenAddress, + isSingle, + secondTokenAddress, + marketInfo, + secondTokenAmount, + setFocusedInput, + setSecondTokenAddress, + setSecondTokenInputValue, + isPair, + chainId, + }); + // #endregion return ( -
+
- { - e.preventDefault(); - submitState.onSubmit(); - }} - > +
minResidualAmount - : true)) || - false - } + onClickTopRightLabel={isDeposit ? onMaxClickFirstToken : undefined} + showMaxButton={firstTokenShowMaxButton} inputValue={firstTokenInputValue} - onInputValueChange={(e) => { - if (firstToken) { - setFirstTokenInputValue(e.target.value); - onFocusedCollateralInputChange(firstToken.address); - } - }} + onInputValueChange={handleFirstTokenInputValueChange} onClickMax={onMaxClickFirstToken} > {firstTokenAddress && isSingle ? ( @@ -937,7 +751,7 @@ export function GmSwapBox(p: Props) { label={isDeposit ? t`Pay` : t`Receive`} chainId={chainId} tokenAddress={firstTokenAddress} - onSelectToken={(token) => setFirstTokenAddress(token.address)} + onSelectToken={firstTokenSelectToken} tokens={tokenOptions} infoTokens={infoTokens} className="GlpSwap-from-token" @@ -961,26 +775,9 @@ export function GmSwapBox(p: Props) { })} preventFocusOnLabelClick="right" inputValue={secondTokenInputValue} - showMaxButton={ - (isDeposit && - secondToken?.balance && - (secondTokenAmount === undefined || secondTokenAmount !== secondToken.balance) && - (secondToken?.isNative - ? minResidualAmount !== undefined && - secondToken?.balance !== undefined && - secondToken.balance > minResidualAmount - : true)) || - false - } - onInputValueChange={(e) => { - if (secondToken) { - setSecondTokenInputValue(e.target.value); - onFocusedCollateralInputChange(secondToken.address); - } - }} - {...(isDeposit && { - onClickTopRightLabel: onMaxClickSecondToken, - })} + showMaxButton={secondTokenShowMaxButton} + onInputValueChange={secondTokenInputValueChange} + onClickTopRightLabel={onMaxClickSecondToken} onClickMax={onMaxClickSecondToken} >
@@ -989,11 +786,7 @@ export function GmSwapBox(p: Props) { )} -
-
- -
-
+ { - setMarketTokenInputValue(e.target.value); - setFocusedInput("market"); - }} - {...(isWithdrawal && { - onClickTopRightLabel: () => { - if (marketToken?.balance) { - setMarketTokenInputValue(formatAmountFree(marketToken.balance, marketToken.decimals)); - setFocusedInput("market"); - } - }, - })} - onClickMax={() => { - if (marketToken?.balance) { - const formattedGMBalance = formatAmountFree(marketToken.balance, marketToken.decimals); - const finalGMBalance = isMetamaskMobile - ? limitDecimals(formattedGMBalance, MAX_METAMASK_MOBILE_DECIMALS) - : formattedGMBalance; - setMarketTokenInputValue(finalGMBalance); - setFocusedInput("market"); - } - }} + onInputValueChange={marketTokenInputValueChange} + onClickTopRightLabel={marketTokenInputClickTopRightLabel} + onClickMax={marketTokenInputClickMax} > { - setIndexName(getMarketIndexName(marketInfo)); - onMarketChange(marketInfo.marketTokenAddress); - showMarketToast(marketInfo); - }} + onSelectMarket={marketTokenSelectMarket} {...gmTokenFavoritesContext} />
- - - { - onMarketChange(marketInfo.marketTokenAddress); - showMarketToast(marketInfo); - }} - {...gmTokenFavoritesContext} - /> - } - /> - - - -
- - -
-
- - {isHighPriceImpact && ( - - - {isSingle ? ( - Acknowledge high Price Impact} - position="top-start" - renderContent={() => ( -
{t`Consider selecting and using the "Pair" option to reduce the Price Impact.`}
- )} - /> - ) : ( - - Acknowledge high Price Impact - - )} -
-
- )} -
+
From 04c97160a887efa9dd0adbcc91d1e50b743af266 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Mon, 5 Aug 2024 16:06:40 +0000 Subject: [PATCH 18/62] Shifts v0 --- .../GmConfirmationBox/GmConfirmationBox.tsx | 228 +++-- .../Synthetics/GmSwap/GmFees/GmFees.tsx | 20 +- .../GmDepositWithdrawalBox.tsx | 794 +++++++++++++++++ .../{ => GmDepositWithdrawalBox}/InfoRows.tsx | 15 +- .../useDepositWithdrawalAmounts.tsx | 4 +- .../useGmDepositWithdrawalBoxState.tsx} | 5 +- .../useUpdateByQueryParams.tsx | 2 +- .../GmSwapBox/GmShiftBox/GmShiftBox.tsx | 383 ++++++++ .../GmShiftBox/getShiftAvailableMarkets.tsx | 36 + .../getShiftAvailableRelatedMarkets.tsx | 37 + .../GmShiftBox/useShiftAvailableMarkets.tsx | 17 + .../useShiftAvailableRelatedMarkets.tsx | 23 + .../GmSwapBox/GmShiftBox/useUpdateMarkets.tsx | 83 ++ .../Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx | 826 +----------------- .../GmSwapBox/getGmSwapBoxAvailableModes.tsx | 2 +- .../Synthetics/GmSwap/GmSwapBox/types.ts | 10 + .../GmSwapBox/useUpdateInputAmounts.tsx | 4 +- .../GmSwap/GmSwapBox/useUpdateTokens.tsx | 9 +- .../GmStatusNotification.tsx | 154 +++- src/config/contracts.ts | 3 + src/config/dataStore.ts | 5 + .../SyntheticsEventsProvider.tsx | 74 +- src/context/SyntheticsEvents/types.ts | 30 + src/context/SyntheticsEvents/utils.ts | 12 +- .../WebsocketContext/subscribeToEvents.ts | 29 +- src/domain/synthetics/fees/types.ts | 1 + src/domain/synthetics/fees/useGasLimits.ts | 6 + .../fees/utils/estimateOraclePriceCount.ts | 4 + .../synthetics/fees/utils/executionFee.ts | 7 + .../synthetics/markets/createDepositTxn.ts | 6 +- .../synthetics/markets/createShiftTxn.ts | 85 ++ .../synthetics/markets/createWithdrawalTxn.ts | 6 +- .../orders/createDecreaseOrderTxn.ts | 6 +- .../orders/createIncreaseOrderTxn.ts | 6 +- .../synthetics/orders/createSwapOrderTxn.ts | 6 +- ...uteOrderTxn.tsx => simulateExecuteTxn.tsx} | 14 +- src/domain/synthetics/trade/types.ts | 1 + src/domain/synthetics/trade/utils/deposit.ts | 36 +- src/domain/synthetics/trade/utils/shift.ts | 120 +++ .../synthetics/trade/utils/validation.ts | 73 ++ .../synthetics/trade/utils/withdrawal.ts | 32 +- src/lib/errorReporting.ts | 2 +- src/pages/MarketPoolsPage/MarketPoolsPage.tsx | 5 +- 43 files changed, 2267 insertions(+), 954 deletions(-) create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx rename src/components/Synthetics/GmSwap/GmSwapBox/{ => GmDepositWithdrawalBox}/InfoRows.tsx (94%) rename src/components/Synthetics/GmSwap/GmSwapBox/{ => GmDepositWithdrawalBox}/useDepositWithdrawalAmounts.tsx (100%) rename src/components/Synthetics/GmSwap/GmSwapBox/{useGmSwapBoxState.tsx => GmDepositWithdrawalBox/useGmDepositWithdrawalBoxState.tsx} (92%) rename src/components/Synthetics/GmSwap/GmSwapBox/{ => GmDepositWithdrawalBox}/useUpdateByQueryParams.tsx (98%) create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/getShiftAvailableMarkets.tsx create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/getShiftAvailableRelatedMarkets.tsx create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableMarkets.tsx create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableRelatedMarkets.tsx create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useUpdateMarkets.tsx create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/types.ts create mode 100644 src/domain/synthetics/markets/createShiftTxn.ts rename src/domain/synthetics/orders/{simulateExecuteOrderTxn.tsx => simulateExecuteTxn.tsx} (93%) create mode 100644 src/domain/synthetics/trade/utils/shift.ts diff --git a/src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx b/src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx index df20164ef3..8a8a7105af 100644 --- a/src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +++ b/src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx @@ -1,36 +1,45 @@ -import { Trans, plural, t } from "@lingui/macro"; -import { ApproveTokenButton } from "components/ApproveTokenButton/ApproveTokenButton"; -import Modal from "components/Modal/Modal"; +import { Trans, msg, plural, t } from "@lingui/macro"; +import { useLingui } from "@lingui/react"; +import { uniq } from "lodash"; +import { useMemo, useState } from "react"; +import { FaArrowRight } from "react-icons/fa"; +import { useKey } from "react-use"; + import { getContract } from "config/contracts"; +import { DEFAULT_SLIPPAGE_AMOUNT } from "config/factors"; +import { useSyntheticsEvents } from "context/SyntheticsEvents"; +import { useMarketsInfoData } from "context/SyntheticsStateContext/hooks/globalsHooks"; import { ExecutionFee } from "domain/synthetics/fees"; import { createDepositTxn } from "domain/synthetics/markets/createDepositTxn"; +import { createShiftTxn } from "domain/synthetics/markets/createShiftTxn"; import { createWithdrawalTxn } from "domain/synthetics/markets/createWithdrawalTxn"; +import { useMarketTokensData } from "domain/synthetics/markets/useMarketTokensData"; import { getNeedTokenApprove, getTokenData, useTokensDataRequest } from "domain/synthetics/tokens"; import { TokenData } from "domain/synthetics/tokens/types"; import { useTokensAllowanceData } from "domain/synthetics/tokens/useTokenAllowanceData"; import { GmSwapFees } from "domain/synthetics/trade"; +import { useHighExecutionFeeConsent } from "domain/synthetics/trade/useHighExecutionFeeConsent"; import { useChainId } from "lib/chains"; import { formatTokenAmount, formatUsd } from "lib/numbers"; import { getByKey } from "lib/objects"; -import { uniq } from "lodash"; -import { GmFees } from "../GmFees/GmFees"; +import { usePendingTxns } from "lib/usePendingTxns"; +import useWallet from "lib/wallets/useWallet"; +import { Operation } from "../GmSwapBox/types"; +import { ApproveTokenButton } from "components/ApproveTokenButton/ApproveTokenButton"; import Button from "components/Button/Button"; -import { DEFAULT_SLIPPAGE_AMOUNT } from "config/factors"; -import { useSyntheticsEvents } from "context/SyntheticsEvents"; -import { useState } from "react"; -import "./GmConfirmationBox.scss"; -import { useKey } from "react-use"; -import useWallet from "lib/wallets/useWallet"; -import { useHighExecutionFeeConsent } from "domain/synthetics/trade/useHighExecutionFeeConsent"; -import { FaArrowRight } from "react-icons/fa"; +import Modal from "components/Modal/Modal"; import { NetworkFeeRow } from "components/Synthetics/NetworkFeeRow/NetworkFeeRow"; -import { usePendingTxns } from "lib/usePendingTxns"; -import { useMarketsInfoData } from "context/SyntheticsStateContext/hooks/globalsHooks"; +import { GmFees } from "../GmFees/GmFees"; + +import "./GmConfirmationBox.scss"; type Props = { isVisible: boolean; marketToken?: TokenData; + fromMarketToken?: TokenData; + fromMarketTokenAmount?: bigint; + fromMarketTokenUsd?: bigint; longToken?: TokenData; shortToken?: TokenData; marketTokenAmount: bigint; @@ -41,13 +50,25 @@ type Props = { shortTokenUsd?: bigint; fees?: GmSwapFees; error?: string; - isDeposit: boolean; + operation: Operation; executionFee?: ExecutionFee; onSubmitted: () => void; onClose: () => void; shouldDisableValidation?: boolean; }; +const operationTextMap = { + [Operation.Deposit]: msg`Buy`, + [Operation.Withdrawal]: msg`Sell`, + [Operation.Shift]: msg`Shift`, +}; + +const processingTextMap = { + [Operation.Deposit]: msg`Buying GM...`, + [Operation.Withdrawal]: msg`Selling GM...`, + [Operation.Shift]: msg`Shifting GM...`, +}; + export function GmConfirmationBox({ isVisible, marketToken, @@ -61,17 +82,22 @@ export function GmConfirmationBox({ shortTokenUsd, fees, error, - isDeposit, + operation, executionFee, onSubmitted, onClose, shouldDisableValidation, + fromMarketToken, + fromMarketTokenAmount, + fromMarketTokenUsd, }: Props) { const { signer, account } = useWallet(); const { chainId } = useChainId(); + const { _ } = useLingui(); const marketsInfoData = useMarketsInfoData(); const { tokensData } = useTokensDataRequest(chainId); - const { setPendingDeposit, setPendingWithdrawal } = useSyntheticsEvents(); + const { marketTokensData } = useMarketTokensData(chainId, { isDeposit: true }); + const { setPendingDeposit, setPendingWithdrawal, setPendingShift } = useSyntheticsEvents(); const [isSubmitting, setIsSubmitting] = useState(false); const [, setPendingTxns] = usePendingTxns(); @@ -89,15 +115,17 @@ export function GmConfirmationBox({ const addresses: string[] = []; - if (isDeposit) { + if (operation === Operation.Deposit) { if (longTokenAmount !== undefined && longTokenAmount > 0 && longToken) { addresses.push(longToken.address); } if (shortTokenAmount !== undefined && shortTokenAmount > 0 && shortToken) { addresses.push(shortToken.address); } - } else { + } else if (operation === Operation.Withdrawal) { addresses.push(marketToken.address); + } else if (operation === Operation.Shift) { + addresses.push(fromMarketToken!.address); } return uniq(addresses); @@ -109,48 +137,71 @@ export function GmConfirmationBox({ skip: !isVisible, }); - const tokensToApprove = (function getTokensToApprove() { - const addresses: string[] = []; - - if (!tokensAllowanceData) { - return addresses; - } + const tokensToApprove = useMemo( + function getTokensToApprove() { + const addresses: string[] = []; - if (isDeposit) { - if ( - longTokenAmount !== undefined && - longTokenAmount > 0 && - longToken && - getNeedTokenApprove(tokensAllowanceData, longToken?.address, longTokenAmount) - ) { - addresses.push(longToken.address); + if (!tokensAllowanceData) { + return addresses; } - if ( - shortTokenAmount !== undefined && - shortTokenAmount > 0 && - shortToken && - getNeedTokenApprove(tokensAllowanceData, shortToken?.address, shortTokenAmount) - ) { - addresses.push(shortToken.address); - } - } else { - if ( - marketTokenAmount > 0 && - marketToken && - getNeedTokenApprove(tokensAllowanceData, marketToken.address, marketTokenAmount) - ) { - addresses.push(marketToken.address); + if (operation === Operation.Deposit) { + if ( + longTokenAmount !== undefined && + longTokenAmount > 0 && + longToken && + getNeedTokenApprove(tokensAllowanceData, longToken?.address, longTokenAmount) + ) { + addresses.push(longToken.address); + } + + if ( + shortTokenAmount !== undefined && + shortTokenAmount > 0 && + shortToken && + getNeedTokenApprove(tokensAllowanceData, shortToken?.address, shortTokenAmount) + ) { + addresses.push(shortToken.address); + } + } else if (operation === Operation.Withdrawal) { + if ( + marketTokenAmount > 0 && + marketToken && + getNeedTokenApprove(tokensAllowanceData, marketToken.address, marketTokenAmount) + ) { + addresses.push(marketToken.address); + } + } else if (operation === Operation.Shift) { + if ( + fromMarketTokenAmount !== undefined && + fromMarketTokenAmount > 0 && + fromMarketToken && + getNeedTokenApprove(tokensAllowanceData, fromMarketToken.address, fromMarketTokenAmount) + ) { + addresses.push(fromMarketToken.address); + } } - } - return uniq(addresses); - })(); + return uniq(addresses); + }, + [ + fromMarketToken, + fromMarketTokenAmount, + longToken, + longTokenAmount, + marketToken, + marketTokenAmount, + operation, + shortToken, + shortTokenAmount, + tokensAllowanceData, + ] + ); const longSymbol = market?.isSameCollaterals ? `${longToken?.symbol} Long` : longToken?.symbol; const shortSymbol = market?.isSameCollaterals ? `${shortToken?.symbol} Short` : shortToken?.symbol; - const operationText = isDeposit ? t`Buy` : t`Sell`; + const operationText = _(operationTextMap[operation]); const isAllowanceLoaded = Boolean(tokensAllowanceData); @@ -167,10 +218,12 @@ export function GmConfirmationBox({ let txnPromise: Promise; - if (isDeposit) { + if (operation === Operation.Deposit) { txnPromise = onCreateDeposit(); - } else { + } else if (operation === Operation.Withdrawal) { txnPromise = onCreateWithdrawal(); + } else { + txnPromise = onCreateShift(); } txnPromise @@ -202,14 +255,14 @@ export function GmConfirmationBox({ if (isSubmitting) { return { - text: isDeposit ? t`Buying GM...` : t`Selling GM...`, + text: _(processingTextMap[operation]), disabled: true, }; } if (tokensToApprove.length > 0 && marketToken) { const symbols = tokensToApprove.map((address) => { - const token = getTokenData(tokensData, address)!; + const token = getTokenData(tokensData, address) || getTokenData(marketTokensData, address); return address === marketToken.address ? "GM" : token?.assetSymbol ?? token?.symbol; }); @@ -224,7 +277,7 @@ export function GmConfirmationBox({ }; } - const operationText = isDeposit ? t`Buy` : `Sell`; + const operationText = _(operationTextMap[operation]); const text = t`Confirm ${operationText}`; return { @@ -314,6 +367,35 @@ export function GmConfirmationBox({ }); } + function onCreateShift() { + if ( + !signer || + !account || + !fromMarketToken || + !executionFee || + !marketToken || + fromMarketTokenAmount === undefined || + marketTokenAmount === undefined || + !tokensData + ) { + return Promise.resolve(); + } + + return createShiftTxn(chainId, signer, { + account, + fromMarketTokenAddress: fromMarketToken.address, + fromMarketTokenAmount: fromMarketTokenAmount, + toMarketTokenAddress: marketToken.address, + minToMarketTokenAmount: marketTokenAmount, + executionFee: executionFee.feeTokenAmount, + allowedSlippage: DEFAULT_SLIPPAGE_AMOUNT, + skipSimulation: shouldDisableValidation, + tokensData, + setPendingTxns, + setPendingShift, + }); + } + const renderTokenInfo = ({ amount, className, @@ -349,7 +431,7 @@ export function GmConfirmationBox({ {isVisible && ( <> - {isDeposit && ( + {operation === Operation.Deposit && (
Pay{" "} @@ -388,7 +470,7 @@ export function GmConfirmationBox({
)} - {!isDeposit && ( + {operation === Operation.Withdrawal && (
Pay{" "} @@ -427,11 +509,32 @@ export function GmConfirmationBox({
)} + {operation === Operation.Shift && ( +
+
+ Pay{" "} + {renderTokenInfo({ + amount: fromMarketTokenAmount, + usd: fromMarketTokenUsd, + token: fromMarketToken, + })} +
+ +
+ Receive{" "} + {renderTokenInfo({ + amount: marketTokenAmount, + usd: marketTokenUsd, + token: marketToken, + })} +
+
+ )}
{ const token = getTokenData(tokensData, address)!; const marketTokenData = - address === marketToken?.address && getByKey(marketsInfoData, marketToken?.address); + (address === marketToken?.address || address === fromMarketToken?.address) && + getByKey(marketsInfoData, marketToken?.address); return (
diff --git a/src/components/Synthetics/GmSwap/GmFees/GmFees.tsx b/src/components/Synthetics/GmSwap/GmFees/GmFees.tsx index 5b1c340ed7..61041af8f5 100644 --- a/src/components/Synthetics/GmSwap/GmFees/GmFees.tsx +++ b/src/components/Synthetics/GmSwap/GmFees/GmFees.tsx @@ -18,6 +18,7 @@ type Props = { swapFee?: FeeItem; swapPriceImpact?: FeeItem; uiFee?: FeeItem; + shiftFee?: FeeItem; isDeposit: boolean; }; @@ -77,6 +78,14 @@ export function GmFees(p: Props) { textClassName="text-red-500" /> )} + + {p.shiftFee !== undefined && ( + Shift Fee} + value={formatDeltaUsd(p.shiftFee.deltaUsd, p.shiftFee.bps)!} + showDollar={false} + /> + )}
)} /> @@ -87,7 +96,16 @@ export function GmFees(p: Props) { {formatDeltaUsd(totalFeesUsd)} ); - }, [p.isDeposit, p.swapFee, p.swapPriceImpact, p.totalFees?.deltaUsd, p.uiFee?.bps, p.uiFee?.deltaUsd, totalFeesUsd]); + }, [ + p.isDeposit, + p.shiftFee, + p.swapFee, + p.swapPriceImpact, + p.totalFees?.deltaUsd, + p.uiFee?.bps, + p.uiFee?.deltaUsd, + totalFeesUsd, + ]); return Fees and Price Impact} value={value} />; } diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx new file mode 100644 index 0000000000..254012b877 --- /dev/null +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx @@ -0,0 +1,794 @@ +import { t } from "@lingui/macro"; +import { useConnectModal } from "@rainbow-me/rainbowkit"; +import cx from "classnames"; + +import { HIGH_PRICE_IMPACT_BPS } from "config/factors"; +import { NATIVE_TOKEN_ADDRESS } from "config/tokens"; +import { MAX_METAMASK_MOBILE_DECIMALS } from "config/ui"; +import { useSettings } from "context/SettingsContext/SettingsContextProvider"; +import { useMarketsInfoData, useTokensData } from "context/SyntheticsStateContext/hooks/globalsHooks"; +import { useHasOutdatedUi } from "domain/legacy"; +import { + FeeItem, + estimateExecuteDepositGasLimit, + estimateExecuteWithdrawalGasLimit, + getExecutionFee, + getFeeItem, + getTotalFeeItem, + useGasLimits, + useGasPrice, +} from "domain/synthetics/fees"; +import { + estimateDepositOraclePriceCount, + estimateWithdrawalOraclePriceCount, +} from "domain/synthetics/fees/utils/estimateOraclePriceCount"; +import useUiFeeFactor from "domain/synthetics/fees/utils/useUiFeeFactor"; +import { useMarketTokensData } from "domain/synthetics/markets"; +import type { MarketInfo } from "domain/synthetics/markets/types"; +import { + getAvailableUsdLiquidityForCollateral, + getMarketIndexName, + getTokenPoolType, +} from "domain/synthetics/markets/utils"; +import { TokenData, convertToUsd, getTokenData } from "domain/synthetics/tokens"; +import { useGmTokensFavorites } from "domain/synthetics/tokens/useGmTokensFavorites"; +import { GmSwapFees, useAvailableTokenOptions } from "domain/synthetics/trade"; +import useSortedPoolsWithIndexToken from "domain/synthetics/trade/useSortedPoolsWithIndexToken"; +import { getCommonError, getGmSwapError } from "domain/synthetics/trade/utils/validation"; +import { Token, getMinResidualAmount } from "domain/tokens"; +import { bigMath } from "lib/bigmath"; +import { useChainId } from "lib/chains"; +import { formatAmountFree, formatTokenAmount, formatUsd, limitDecimals, parseValue } from "lib/numbers"; +import { getByKey } from "lib/objects"; +import useIsMetamaskMobile from "lib/wallets/useIsMetamaskMobile"; +import useWallet from "lib/wallets/useWallet"; +import { useCallback, useEffect, useMemo } from "react"; +import type { GmSwapBoxProps } from "../GmSwapBox"; +import { showMarketToast } from "../showMarketToast"; +import { Mode, Operation } from "../types"; +import { useUpdateInputAmounts } from "../useUpdateInputAmounts"; +import { useUpdateTokens } from "../useUpdateTokens"; +import { useDepositWithdrawalAmounts } from "./useDepositWithdrawalAmounts"; +import { useGmDepositWithdrawalBoxState } from "./useGmDepositWithdrawalBoxState"; +import { useUpdateByQueryParams } from "./useUpdateByQueryParams"; + +import Button from "components/Button/Button"; +import BuyInputSection from "components/BuyInputSection/BuyInputSection"; +import { PoolSelector } from "components/MarketSelector/PoolSelector"; +import TokenWithIcon from "components/TokenIcon/TokenWithIcon"; +import TokenSelector from "components/TokenSelector/TokenSelector"; +import { GmConfirmationBox } from "../../GmConfirmationBox/GmConfirmationBox"; +import { Swap } from "../Swap"; +import { InfoRows } from "./InfoRows"; + +export function GmSwapBoxDepositWithdrawal(p: GmSwapBoxProps) { + const { selectedMarketAddress, operation, mode, onSetMode, onSetOperation, onSelectMarket } = p; + const isMetamaskMobile = useIsMetamaskMobile(); + const { openConnectModal } = useConnectModal(); + + const marketAddress = selectedMarketAddress; + const { shouldDisableValidationForTesting } = useSettings(); + + const { chainId } = useChainId(); + const { account } = useWallet(); + + // #region Requests + const { marketTokensData: depositMarketTokensData } = useMarketTokensData(chainId, { isDeposit: true }); + const { marketTokensData: withdrawalMarketTokensData } = useMarketTokensData(chainId, { isDeposit: false }); + const gasLimits = useGasLimits(chainId); + const gasPrice = useGasPrice(chainId); + const uiFeeFactor = useUiFeeFactor(chainId); + const { data: hasOutdatedUi } = useHasOutdatedUi(); + // #endregion + // #region Selectors + const marketsInfoData = useMarketsInfoData(); + const { marketsInfo: sortedMarketsInfoByIndexToken } = useSortedPoolsWithIndexToken( + marketsInfoData, + depositMarketTokensData + ); + const tokensData = useTokensData(); + const { infoTokens } = useAvailableTokenOptions(chainId, { marketsInfoData, tokensData }); + + // #region State + const gmTokenFavoritesContext = useGmTokensFavorites(); + const { + focusedInput, + setFocusedInput, + stage, + setStage, + isHighPriceImpactAccepted, + setIsHighPriceImpactAccepted, + indexName, + setIndexName, + firstTokenAddress, + setFirstTokenAddress, + secondTokenAddress, + setSecondTokenAddress, + firstTokenInputValue, + setFirstTokenInputValue, + secondTokenInputValue, + setSecondTokenInputValue, + marketTokenInputValue, + setMarketTokenInputValue, + } = useGmDepositWithdrawalBoxState(operation, mode, marketAddress); + // #endregion + // #region Derived state + const nativeToken = getByKey(tokensData, NATIVE_TOKEN_ADDRESS); + const minResidualAmount = getMinResidualAmount(nativeToken?.decimals, nativeToken?.prices?.maxPrice); + + const isDeposit = operation === Operation.Deposit; + const isWithdrawal = operation === Operation.Withdrawal; + const isSingle = mode === Mode.Single; + const isPair = mode === Mode.Pair; + + const marketTokensData = isDeposit ? depositMarketTokensData : withdrawalMarketTokensData; + + const marketInfo = getByKey(marketsInfoData, marketAddress); + + let firstToken = getTokenData(tokensData, firstTokenAddress); + let firstTokenAmount = parseValue(firstTokenInputValue, firstToken?.decimals || 0); + const firstTokenUsd = convertToUsd( + firstTokenAmount, + firstToken?.decimals, + isDeposit ? firstToken?.prices?.minPrice : firstToken?.prices?.maxPrice + ); + + let secondToken = getTokenData(tokensData, secondTokenAddress); + let secondTokenAmount = parseValue(secondTokenInputValue, secondToken?.decimals || 0); + const secondTokenUsd = convertToUsd( + secondTokenAmount, + secondToken?.decimals, + isDeposit ? secondToken?.prices?.minPrice : secondToken?.prices?.maxPrice + ); + + const { + longTokenInputState, + // Undefined when isSameCollaterals is true + shortTokenInputState, + } = useMemo(() => { + if (!marketInfo) { + return {}; + } + + const inputs: { + address: string; + value: string; + amount?: bigint; + usd?: bigint; + token?: TokenData; + setValue: (val: string) => void; + }[] = []; + + if (firstTokenAddress) { + inputs.push({ + address: firstTokenAddress, + value: firstTokenInputValue, + setValue: setFirstTokenInputValue, + amount: firstTokenAmount, + usd: firstTokenUsd, + token: firstToken, + }); + } + + if (isPair && secondTokenAddress) { + inputs.push({ + address: secondTokenAddress, + value: secondTokenInputValue, + setValue: setSecondTokenInputValue, + amount: secondTokenAmount, + usd: secondTokenUsd, + token: secondToken, + }); + } + + const longTokenInputState = inputs.find((input) => getTokenPoolType(marketInfo, input.address) === "long"); + const shortTokenInputState = inputs.find((input) => getTokenPoolType(marketInfo, input.address) === "short"); + + return { + longTokenInputState, + shortTokenInputState, + }; + }, [ + firstToken, + firstTokenAddress, + firstTokenAmount, + firstTokenInputValue, + firstTokenUsd, + isPair, + marketInfo, + secondToken, + secondTokenAddress, + secondTokenAmount, + secondTokenInputValue, + secondTokenUsd, + setFirstTokenInputValue, + setSecondTokenInputValue, + ]); + + const tokenOptions: Token[] = useMemo( + function getTokenOptions(): TokenData[] { + const { longToken, shortToken } = marketInfo || {}; + + if (!longToken || !shortToken) return []; + + const result = [longToken]; + + if (longToken.address !== shortToken.address) { + result.push(shortToken); + } + + const nativeToken = getByKey(tokensData, NATIVE_TOKEN_ADDRESS)!; + + if (result.some((token) => token.isWrapped) && nativeToken) { + result.unshift(nativeToken); + } + + return result; + }, + [marketInfo, tokensData] + ); + + const marketToken = getTokenData( + isDeposit ? depositMarketTokensData : withdrawalMarketTokensData, + marketInfo?.marketTokenAddress + ); + const marketTokenAmount = parseValue(marketTokenInputValue || "0", marketToken?.decimals || 0)!; + const marketTokenUsd = convertToUsd( + marketTokenAmount, + marketToken?.decimals, + isDeposit ? marketToken?.prices?.maxPrice : marketToken?.prices?.minPrice + )!; + + const { longCollateralLiquidityUsd, shortCollateralLiquidityUsd } = useMemo(() => { + if (!marketInfo) return {}; + + return { + longCollateralLiquidityUsd: getAvailableUsdLiquidityForCollateral(marketInfo, true), + shortCollateralLiquidityUsd: getAvailableUsdLiquidityForCollateral(marketInfo, false), + }; + }, [marketInfo]); + + const amounts = useDepositWithdrawalAmounts({ + isDeposit, + marketInfo, + marketToken, + longTokenInputState, + shortTokenInputState, + marketTokenAmount, + uiFeeFactor, + focusedInput, + isWithdrawal, + }); + + const { fees, executionFee } = useMemo(() => { + if (!gasLimits || gasPrice === undefined || !tokensData || !amounts) { + return {}; + } + + const basisUsd = isDeposit + ? (amounts?.longTokenUsd ?? 0n) + (amounts?.shortTokenUsd ?? 0n) + : amounts?.marketTokenUsd || 0n; + + const swapFee = getFeeItem(amounts.swapFeeUsd * -1n, basisUsd); + const swapPriceImpact = getFeeItem(amounts.swapPriceImpactDeltaUsd, basisUsd); + const uiFee = getFeeItem(amounts.uiFeeUsd * -1n, basisUsd, { + shouldRoundUp: true, + }); + + const totalFees = getTotalFeeItem([swapPriceImpact, swapFee, uiFee].filter(Boolean) as FeeItem[]); + const fees: GmSwapFees = { + swapFee, + swapPriceImpact, + totalFees, + uiFee, + }; + + const gasLimit = isDeposit + ? estimateExecuteDepositGasLimit(gasLimits, { + initialLongTokenAmount: amounts.longTokenAmount, + initialShortTokenAmount: amounts.shortTokenAmount, + }) + : estimateExecuteWithdrawalGasLimit(gasLimits, {}); + + const oraclePriceCount = isDeposit ? estimateDepositOraclePriceCount(0) : estimateWithdrawalOraclePriceCount(0); + + const executionFee = getExecutionFee(chainId, gasLimits, tokensData, gasLimit, gasPrice, oraclePriceCount); + + return { + fees, + executionFee, + }; + }, [amounts, chainId, gasLimits, gasPrice, isDeposit, tokensData]); + + const isHighPriceImpact = + (fees?.swapPriceImpact?.deltaUsd ?? 0) < 0 && + bigMath.abs(fees?.swapPriceImpact?.bps ?? 0n) >= HIGH_PRICE_IMPACT_BPS; + + const submitState = useMemo(() => { + const commonError = getCommonError({ + chainId, + isConnected: true, + hasOutdatedUi, + })[0]; + + const swapError = getGmSwapError({ + isDeposit, + marketInfo, + marketToken, + longToken: longTokenInputState?.token, + shortToken: shortTokenInputState?.token, + marketTokenAmount, + marketTokenUsd: amounts?.marketTokenUsd, + longTokenAmount: amounts?.longTokenAmount, + shortTokenAmount: amounts?.shortTokenAmount, + longTokenUsd: amounts?.longTokenUsd, + shortTokenUsd: amounts?.shortTokenUsd, + longTokenLiquidityUsd: longCollateralLiquidityUsd, + shortTokenLiquidityUsd: shortCollateralLiquidityUsd, + fees, + isHighPriceImpact: Boolean(isHighPriceImpact), + isHighPriceImpactAccepted, + priceImpactUsd: fees?.swapPriceImpact?.deltaUsd, + })[0]; + + const error = commonError || swapError; + + if (!account) { + return { + text: t`Connect Wallet`, + onSubmit: () => openConnectModal?.(), + }; + } + + const onSubmit = () => { + setStage("confirmation"); + }; + + if (error) { + return { + text: error, + error, + isDisabled: !shouldDisableValidationForTesting, + onSubmit, + }; + } + + return { + text: isDeposit ? t`Buy GM` : t`Sell GM`, + onSubmit, + }; + }, [ + chainId, + hasOutdatedUi, + isDeposit, + marketInfo, + marketToken, + longTokenInputState?.token, + shortTokenInputState?.token, + marketTokenAmount, + amounts?.marketTokenUsd, + amounts?.longTokenAmount, + amounts?.shortTokenAmount, + amounts?.longTokenUsd, + amounts?.shortTokenUsd, + longCollateralLiquidityUsd, + shortCollateralLiquidityUsd, + fees, + isHighPriceImpact, + isHighPriceImpactAccepted, + account, + openConnectModal, + setStage, + shouldDisableValidationForTesting, + ]); + + const firstTokenShowMaxButton = + (isDeposit && + firstToken?.balance && + (firstTokenAmount === undefined || firstTokenAmount !== firstToken.balance) && + (firstToken?.isNative + ? minResidualAmount !== undefined && firstToken?.balance !== undefined && firstToken.balance > minResidualAmount + : true)) || + false; + + const secondTokenShowMaxButton = + (isDeposit && + secondToken?.balance && + (secondTokenAmount === undefined || secondTokenAmount !== secondToken.balance) && + (secondToken?.isNative + ? minResidualAmount !== undefined && + secondToken?.balance !== undefined && + secondToken.balance > minResidualAmount + : true)) || + false; + + const marketTokenInputShowMaxButton = + (isWithdrawal && + marketToken?.balance && + (marketTokenAmount === undefined || marketTokenAmount !== marketToken.balance)) || + false; + // #endregion + // #region Callbacks + const onFocusedCollateralInputChange = useCallback( + (tokenAddress: string) => { + if (!marketInfo) { + return; + } + + if (marketInfo.isSameCollaterals) { + setFocusedInput("longCollateral"); + return; + } + + if (getTokenPoolType(marketInfo, tokenAddress) === "long") { + setFocusedInput("longCollateral"); + } else { + setFocusedInput("shortCollateral"); + } + }, + [marketInfo, setFocusedInput] + ); + + const resetInputs = useCallback(() => { + setFirstTokenInputValue(""); + setSecondTokenInputValue(""); + setMarketTokenInputValue(""); + }, [setFirstTokenInputValue, setMarketTokenInputValue, setSecondTokenInputValue]); + + const onSwitchSide = useCallback(() => { + setFocusedInput("market"); + resetInputs(); + onSetOperation(operation === Operation.Deposit ? Operation.Withdrawal : Operation.Deposit); + }, [operation, resetInputs, setFocusedInput, onSetOperation]); + + const onMarketChange = useCallback( + (marketAddress: string) => { + resetInputs(); + onSelectMarket(marketAddress); + }, + [onSelectMarket, resetInputs] + ); + + const onMaxClickFirstToken = useCallback(() => { + if (firstToken?.balance) { + let maxAvailableAmount = firstToken.isNative + ? firstToken.balance - (minResidualAmount ?? 0n) + : firstToken.balance; + + if (maxAvailableAmount < 0) { + maxAvailableAmount = 0n; + } + + const formattedMaxAvailableAmount = formatAmountFree(maxAvailableAmount, firstToken.decimals); + const finalAmount = isMetamaskMobile + ? limitDecimals(formattedMaxAvailableAmount, MAX_METAMASK_MOBILE_DECIMALS) + : formattedMaxAvailableAmount; + + setFirstTokenInputValue(finalAmount); + onFocusedCollateralInputChange(firstToken.address); + } + }, [ + firstToken?.address, + firstToken?.balance, + firstToken?.decimals, + firstToken?.isNative, + isMetamaskMobile, + minResidualAmount, + onFocusedCollateralInputChange, + setFirstTokenInputValue, + ]); + + const onMaxClickSecondToken = useCallback(() => { + if (!isDeposit) { + return; + } + + if (secondToken?.balance === undefined) { + return; + } + + let maxAvailableAmount = secondToken.isNative + ? secondToken.balance - (minResidualAmount ?? 0n) + : secondToken.balance; + + if (maxAvailableAmount < 0) { + maxAvailableAmount = 0n; + } + + const formattedMaxAvailableAmount = formatAmountFree(maxAvailableAmount, secondToken.decimals); + const finalAmount = isMetamaskMobile + ? limitDecimals(formattedMaxAvailableAmount, MAX_METAMASK_MOBILE_DECIMALS) + : formattedMaxAvailableAmount; + setSecondTokenInputValue(finalAmount); + onFocusedCollateralInputChange(secondToken.address); + }, [ + isDeposit, + isMetamaskMobile, + minResidualAmount, + onFocusedCollateralInputChange, + secondToken?.address, + secondToken?.balance, + secondToken?.decimals, + secondToken?.isNative, + setSecondTokenInputValue, + ]); + + const handleFirstTokenInputValueChange = useCallback( + (e) => { + if (firstToken) { + setFirstTokenInputValue(e.target.value); + onFocusedCollateralInputChange(firstToken.address); + } + }, + [firstToken, onFocusedCollateralInputChange, setFirstTokenInputValue] + ); + + const handleFormSubmit = useCallback( + (e: React.FormEvent) => { + e.preventDefault(); + submitState.onSubmit(); + }, + [submitState] + ); + + const marketTokenInputClickMax = useCallback(() => { + if (marketToken?.balance) { + const formattedGMBalance = formatAmountFree(marketToken.balance, marketToken.decimals); + const finalGMBalance = isMetamaskMobile + ? limitDecimals(formattedGMBalance, MAX_METAMASK_MOBILE_DECIMALS) + : formattedGMBalance; + setMarketTokenInputValue(finalGMBalance); + setFocusedInput("market"); + } + }, [isMetamaskMobile, marketToken?.balance, marketToken?.decimals, setFocusedInput, setMarketTokenInputValue]); + + const marketTokenInputClickTopRightLabel = useCallback(() => { + if (!isWithdrawal) { + return; + } + + if (marketToken?.balance) { + setMarketTokenInputValue(formatAmountFree(marketToken.balance, marketToken.decimals)); + setFocusedInput("market"); + } + }, [isWithdrawal, marketToken?.balance, marketToken?.decimals, setFocusedInput, setMarketTokenInputValue]); + + const marketTokenInputValueChange = useCallback( + (e: React.ChangeEvent) => { + setMarketTokenInputValue(e.target.value); + setFocusedInput("market"); + }, + [setFocusedInput, setMarketTokenInputValue] + ); + + const secondTokenInputValueChange = useCallback( + (e: React.ChangeEvent) => { + if (secondToken) { + setSecondTokenInputValue(e.target.value); + onFocusedCollateralInputChange(secondToken.address); + } + }, + [onFocusedCollateralInputChange, secondToken, setSecondTokenInputValue] + ); + + const firstTokenSelectToken = useCallback( + (token: Token): void => setFirstTokenAddress(token.address), + [setFirstTokenAddress] + ); + + const marketTokenSelectMarket = useCallback( + (marketInfo: MarketInfo): void => { + setIndexName(getMarketIndexName(marketInfo)); + onMarketChange(marketInfo.marketTokenAddress); + showMarketToast(marketInfo); + }, + [onMarketChange, setIndexName] + ); + // #endregion + // #region Effects + useUpdateInputAmounts({ + marketToken, + marketInfo, + longTokenInputState, + shortTokenInputState, + isDeposit, + focusedInput, + amounts, + setMarketTokenInputValue, + marketTokenAmount, + isWithdrawal, + setFirstTokenInputValue, + setSecondTokenInputValue, + }); + + useEffect( + function updateIndexToken() { + if (!indexName && sortedMarketsInfoByIndexToken.length) { + setIndexName(getMarketIndexName(sortedMarketsInfoByIndexToken[0])); + } + }, + [indexName, sortedMarketsInfoByIndexToken, setIndexName] + ); + + useEffect( + function updateMarket() { + const marketsByIndexName = sortedMarketsInfoByIndexToken.filter( + (market) => getMarketIndexName(market) === indexName + ); + + if (!marketsByIndexName.length) { + return; + } + + if (!marketAddress || !marketsByIndexName.find((market) => market.marketTokenAddress === marketAddress)) { + onMarketChange(marketsByIndexName[0].marketTokenAddress); + } + }, + [indexName, marketAddress, sortedMarketsInfoByIndexToken, onMarketChange] + ); + + useUpdateByQueryParams({ + setOperation: onSetOperation, + setMode: onSetMode, + setIndexName, + onSelectMarket, + setFirstTokenAddress, + }); + + useUpdateTokens({ + tokenOptions, + firstTokenAddress, + setFirstTokenAddress, + isSingle, + secondTokenAddress, + marketInfo, + secondTokenAmount, + setFocusedInput, + setSecondTokenAddress, + setSecondTokenInputValue, + isPair, + chainId, + }); + // #endregion + return ( + <> + +
+ + {firstTokenAddress && isSingle && isDeposit ? ( + + ) : ( +
+ +
+ )} +
+ + {isPair && secondTokenAddress && ( + +
+ +
+
+ )} + + + + + + +
+ + + +
+ +
+ + + { + setStage("swap"); + }} + onClose={() => { + setStage("swap"); + }} + shouldDisableValidation={shouldDisableValidationForTesting} + /> + + ); +} diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/InfoRows.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx similarity index 94% rename from src/components/Synthetics/GmSwap/GmSwapBox/InfoRows.tsx rename to src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx index b653a34b9d..247e0b501a 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/InfoRows.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx @@ -1,19 +1,18 @@ import { t } from "@lingui/macro"; -import { ExecutionFee } from "domain/synthetics/fees"; -import { MarketInfo } from "domain/synthetics/markets/types"; -import { TokensData } from "domain/synthetics/tokens"; -import { useGmTokensFavorites } from "domain/synthetics/tokens/useGmTokensFavorites"; -import { GmSwapFees } from "domain/synthetics/trade"; import { ExchangeInfo } from "components/Exchange/ExchangeInfo"; import ExchangeInfoRow from "components/Exchange/ExchangeInfoRow"; import { PoolSelector } from "components/MarketSelector/PoolSelector"; import { GmFees } from "components/Synthetics/GmSwap/GmFees/GmFees"; import { NetworkFeeRow } from "components/Synthetics/NetworkFeeRow/NetworkFeeRow"; -import { showMarketToast } from "./showMarketToast"; -import { HighPriceImpactRow } from "./HighPriceImpactRow"; -import { useSelector } from "context/SyntheticsStateContext/utils"; import { selectMarketsInfoData } from "context/SyntheticsStateContext/selectors/globalSelectors"; +import { useSelector } from "context/SyntheticsStateContext/utils"; +import { ExecutionFee } from "domain/synthetics/fees"; +import { TokensData } from "domain/synthetics/tokens"; +import { useGmTokensFavorites } from "domain/synthetics/tokens/useGmTokensFavorites"; +import { GmSwapFees } from "domain/synthetics/trade"; import { values } from "lodash"; +import { HighPriceImpactRow } from "../HighPriceImpactRow"; +import { showMarketToast } from "../showMarketToast"; export function InfoRows({ indexName, diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/useDepositWithdrawalAmounts.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useDepositWithdrawalAmounts.tsx similarity index 100% rename from src/components/Synthetics/GmSwap/GmSwapBox/useDepositWithdrawalAmounts.tsx rename to src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useDepositWithdrawalAmounts.tsx index af0f9c4871..a9e9dd0086 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/useDepositWithdrawalAmounts.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useDepositWithdrawalAmounts.tsx @@ -8,6 +8,7 @@ import { DepositAmounts, WithdrawalAmounts } from "domain/synthetics/trade/types export function useDepositWithdrawalAmounts({ isDeposit, + isWithdrawal, marketInfo, marketToken, longTokenInputState, @@ -15,9 +16,9 @@ export function useDepositWithdrawalAmounts({ marketTokenAmount, uiFeeFactor, focusedInput, - isWithdrawal, }: { isDeposit: boolean; + isWithdrawal: boolean; marketInfo: MarketInfo | undefined; marketToken: TokenData | undefined; longTokenInputState: @@ -35,7 +36,6 @@ export function useDepositWithdrawalAmounts({ marketTokenAmount: bigint; uiFeeFactor: bigint; focusedInput: string; - isWithdrawal: boolean; }): DepositAmounts | WithdrawalAmounts | undefined { const halfOfLong = longTokenInputState?.amount !== undefined ? longTokenInputState.amount / 2n : undefined; diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/useGmSwapBoxState.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useGmDepositWithdrawalBoxState.tsx similarity index 92% rename from src/components/Synthetics/GmSwap/GmSwapBox/useGmSwapBoxState.tsx rename to src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useGmDepositWithdrawalBoxState.tsx index 611159e643..dba2ec3634 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/useGmSwapBoxState.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useGmDepositWithdrawalBoxState.tsx @@ -5,9 +5,10 @@ import { useLocalStorageSerializeKey } from "lib/localStorage"; import { useSafeState } from "lib/useSafeState"; import { useSelector } from "context/SyntheticsStateContext/utils"; import { selectChainId } from "context/SyntheticsStateContext/selectors/globalSelectors"; -import { Operation, Mode } from "./GmSwapBox"; -export function useGmSwapBoxState(operation: Operation, mode: Mode, marketAddress: string | undefined) { +import { Operation, Mode } from "../types"; + +export function useGmDepositWithdrawalBoxState(operation: Operation, mode: Mode, marketAddress: string | undefined) { const isDeposit = operation === Operation.Deposit; const chainId = useSelector(selectChainId); diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/useUpdateByQueryParams.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useUpdateByQueryParams.tsx similarity index 98% rename from src/components/Synthetics/GmSwap/GmSwapBox/useUpdateByQueryParams.tsx rename to src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useUpdateByQueryParams.tsx index 641064ca24..81d53199d0 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/useUpdateByQueryParams.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useUpdateByQueryParams.tsx @@ -12,7 +12,7 @@ import { helperToast } from "lib/helperToast"; import { getMatchingValueFromObject } from "lib/objects"; import useSearchParams from "lib/useSearchParams"; -import { Mode, Operation } from "./GmSwapBox"; +import { Mode, Operation } from "../types"; type SearchParams = { market?: string; diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx new file mode 100644 index 0000000000..839e354d5a --- /dev/null +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx @@ -0,0 +1,383 @@ +import { t } from "@lingui/macro"; +import { useConnectModal } from "@rainbow-me/rainbowkit"; +import { useCallback, useEffect, useMemo, useState } from "react"; + +import { HIGH_PRICE_IMPACT_BPS } from "config/factors"; +import { useSettings } from "context/SettingsContext/SettingsContextProvider"; +import { useMarketsInfoData, useTokensData, useUiFeeFactor } from "context/SyntheticsStateContext/hooks/globalsHooks"; +import { + selectAccount, + selectChainId, + selectGasLimits, + selectGasPrice, +} from "context/SyntheticsStateContext/selectors/globalSelectors"; +import { useSelector } from "context/SyntheticsStateContext/utils"; +import { useHasOutdatedUi } from "domain/legacy"; +import { + FeeItem, + estimateExecuteShiftGasLimit, + getExecutionFee, + getFeeItem, + getTotalFeeItem, +} from "domain/synthetics/fees"; +import { estimateShiftOraclePriceCount } from "domain/synthetics/fees/utils/estimateOraclePriceCount"; +import { MarketInfo, getMarketIndexName } from "domain/synthetics/markets"; +import { useMarketTokensData } from "domain/synthetics/markets/useMarketTokensData"; +import { useGmTokensFavorites } from "domain/synthetics/tokens/useGmTokensFavorites"; +import { GmSwapFees } from "domain/synthetics/trade/types"; +import useSortedPoolsWithIndexToken from "domain/synthetics/trade/useSortedPoolsWithIndexToken"; +import { getShiftAmounts } from "domain/synthetics/trade/utils/shift"; +import { getCommonError, getGmShiftError } from "domain/synthetics/trade/utils/validation"; +import { bigMath } from "lib/bigmath"; +import { formatAmountFree, formatTokenAmount, formatUsd, parseValue } from "lib/numbers"; +import { getByKey } from "lib/objects"; +import { Operation } from "../types"; +import { useShiftAvailableMarkets } from "./useShiftAvailableMarkets"; +import { useShiftAvailableRelatedMarkets } from "./useShiftAvailableRelatedMarkets"; +import { useUpdateMarkets } from "./useUpdateMarkets"; + +import Button from "components/Button/Button"; +import BuyInputSection from "components/BuyInputSection/BuyInputSection"; +import { ExchangeInfo } from "components/Exchange/ExchangeInfo"; +import { PoolSelector } from "components/MarketSelector/PoolSelector"; +import { GmConfirmationBox } from "../../GmConfirmationBox/GmConfirmationBox"; +import { GmFees } from "../../GmFees/GmFees"; +import { Swap } from "../Swap"; + +export function GmShiftBox({ + selectedMarketAddress, + onSelectMarket, +}: { + selectedMarketAddress: string | undefined; + onSelectMarket: (marketAddress: string) => void; +}) { + const [toMarketAddress, setToMarketAddress] = useState(undefined); + const [selectedMarketText, setSelectedMarketText] = useState(""); + const [toMarketText, setToMarketText] = useState(""); + const gmTokenFavoritesContext = useGmTokensFavorites(); + const [focusedInput, setFocusedInput] = useState<"selectedMarket" | "toMarket" | undefined>(undefined); + const [isConfirmationBoxVisible, setIsConfirmationBoxVisible] = useState(false); + const { openConnectModal } = useConnectModal(); + + const chainId = useSelector(selectChainId); + const account = useSelector(selectAccount); + const uiFeeFactor = useUiFeeFactor(); + const gasLimits = useSelector(selectGasLimits); + const gasPrice = useSelector(selectGasPrice); + const marketsInfoData = useMarketsInfoData(); + const tokensData = useTokensData(); + const { marketTokensData: depositMarketTokensData } = useMarketTokensData(chainId, { isDeposit: true }); + const { marketsInfo: sortedMarketsInfoByIndexToken } = useSortedPoolsWithIndexToken( + marketsInfoData, + depositMarketTokensData + ); + const shiftAvailableMarkets = useShiftAvailableMarkets(sortedMarketsInfoByIndexToken); + const shiftAvailableRelatedMarkets = useShiftAvailableRelatedMarkets( + marketsInfoData, + sortedMarketsInfoByIndexToken, + selectedMarketAddress + ); + const { shouldDisableValidationForTesting } = useSettings(); + const { data: hasOutdatedUi } = useHasOutdatedUi(); + + const selectedMarketInfo = getByKey(marketsInfoData, selectedMarketAddress); + const selectedIndexName = selectedMarketInfo ? getMarketIndexName(selectedMarketInfo) : "..."; + const selectedToken = getByKey(depositMarketTokensData, selectedMarketAddress); + const toMarketInfo = getByKey(marketsInfoData, toMarketAddress); + const toIndexName = toMarketInfo ? getMarketIndexName(toMarketInfo) : "..."; + const toToken = getByKey(depositMarketTokensData, toMarketAddress); + + const amounts = useMemo(() => { + if (!selectedMarketInfo || !selectedToken || !toMarketInfo || !toToken) { + return; + } + + let fromTokenAmount = parseValue(selectedMarketText, selectedToken.decimals) ?? 0n; + + let toTokenAmount = parseValue(toMarketText, toToken.decimals) ?? 0n; + + const amounts = getShiftAmounts({ + fromMarketInfo: selectedMarketInfo, + fromToken: selectedToken, + fromTokenAmount, + toMarketInfo, + toToken: toToken, + toTokenAmount, + strategy: focusedInput === "selectedMarket" ? "byFromToken" : "byToToken", + uiFeeFactor, + }); + + return amounts; + }, [ + focusedInput, + selectedMarketInfo, + selectedMarketText, + selectedToken, + toMarketInfo, + toMarketText, + toToken, + uiFeeFactor, + ]); + + const { fees, executionFee } = useMemo(() => { + if (!gasLimits || gasPrice === undefined || !tokensData || !amounts) { + return {}; + } + + const basisUsd = amounts.fromTokenUsd; + + const swapPriceImpact = getFeeItem(amounts.swapPriceImpactDeltaUsd, basisUsd); + const uiFee = getFeeItem(amounts.uiFeeUsd * -1n, basisUsd, { + shouldRoundUp: true, + }); + const shiftFee = getFeeItem(0n, basisUsd); + + const totalFees = getTotalFeeItem([swapPriceImpact, uiFee].filter(Boolean) as FeeItem[]); + const fees: GmSwapFees = { + swapPriceImpact, + totalFees, + uiFee, + shiftFee, + }; + + const gasLimit = estimateExecuteShiftGasLimit(gasLimits, { + callbackGasLimit: 0n, + }); + + const oraclePriceCount = estimateShiftOraclePriceCount(); + + const executionFee = getExecutionFee(chainId, gasLimits, tokensData, gasLimit, gasPrice, oraclePriceCount); + + return { + fees, + executionFee, + }; + }, [amounts, chainId, gasLimits, gasPrice, tokensData]); + + const isHighPriceImpact = + (fees?.swapPriceImpact?.deltaUsd ?? 0) < 0 && + bigMath.abs(fees?.swapPriceImpact?.bps ?? 0n) >= HIGH_PRICE_IMPACT_BPS; + + const submitState = useMemo(() => { + if (!account) { + return { + text: t`Connect Wallet`, + onSubmit: () => openConnectModal?.(), + }; + } + + const commonError = getCommonError({ + chainId, + isConnected: true, + hasOutdatedUi, + })[0]; + + const swapError = getGmShiftError({ + fromMarketInfo: selectedMarketInfo, + fromToken: selectedToken, + fromTokenAmount: amounts?.fromTokenAmount, + fromTokenUsd: amounts?.fromTokenUsd, + toMarketInfo: toMarketInfo, + toToken: toToken, + toTokenAmount: amounts?.toTokenAmount, + fees, + isHighPriceImpact: isHighPriceImpact, + isHighPriceImpactAccepted: true, + priceImpactUsd: amounts?.swapPriceImpactDeltaUsd, + })[0]; + + const error = commonError || swapError; + + const onSubmit = () => { + setIsConfirmationBoxVisible(true); + }; + + if (error) { + return { + text: error, + error, + isDisabled: !shouldDisableValidationForTesting, + onSubmit, + }; + } + + return { + text: t`Shift GM`, + onSubmit, + }; + }, [ + account, + chainId, + hasOutdatedUi, + selectedMarketInfo, + selectedToken, + amounts?.fromTokenAmount, + amounts?.fromTokenUsd, + amounts?.toTokenAmount, + amounts?.swapPriceImpactDeltaUsd, + toMarketInfo, + toToken, + fees, + isHighPriceImpact, + openConnectModal, + shouldDisableValidationForTesting, + ]); + + useUpdateMarkets({ + marketsInfoData, + selectedMarketAddress, + shiftAvailableMarkets, + onSelectMarket, + toMarketAddress, + toMarketInfo, + selectedMarketInfo, + setToMarketAddress, + }); + + useEffect( + function updateTokens() { + if (!amounts || !selectedToken || !toToken) { + return; + } + + if (focusedInput === "selectedMarket") { + if (amounts.toTokenAmount === 0n) { + setToMarketText(""); + } else { + setToMarketText(formatAmountFree(amounts.toTokenAmount, toToken.decimals)); + } + } else { + if (amounts.fromTokenAmount === 0n) { + setSelectedMarketText(""); + } else { + setSelectedMarketText(formatAmountFree(amounts.fromTokenAmount, selectedToken.decimals)); + } + } + }, + [amounts, focusedInput, selectedToken, toToken] + ); + + const handleFormSubmit = useCallback( + (event: React.FormEvent) => { + event.preventDefault(); + submitState.onSubmit(); + }, + [submitState] + ); + + const handleSelectedTokenClickTopRightLabel = useCallback(() => { + if (!selectedToken || selectedToken.balance === undefined) return; + setSelectedMarketText(formatAmountFree(selectedToken.balance, selectedToken.decimals)); + }, [selectedToken]); + + const handleSelectedTokenInputValueChange = useCallback( + (event: React.ChangeEvent) => setSelectedMarketText(event.target.value), + [] + ); + + const handleSelectedTokenFocus = useCallback(() => setFocusedInput("selectedMarket"), []); + + const handleSelectedTokenSelectMarket = useCallback( + (marketInfo: MarketInfo): void => onSelectMarket(marketInfo.marketTokenAddress), + [onSelectMarket] + ); + + return ( + <> +
+ + + + { + onSelectMarket(toMarketAddress!); + setToMarketAddress(selectedMarketAddress); + }} + /> + setToMarketText(event.target.value)} + onFocus={() => setFocusedInput("toMarket")} + > + setToMarketAddress(marketInfo.marketTokenAddress)} + selectedIndexName={toIndexName} + showAllPools + isSideMenu + showIndexIcon + showBalances + marketTokensData={depositMarketTokensData} + {...gmTokenFavoritesContext} + /> + + + + + + +
+ +
+ + + { + setIsConfirmationBoxVisible(false); + }} + onClose={() => { + setIsConfirmationBoxVisible(false); + }} + shouldDisableValidation={shouldDisableValidationForTesting} + /> + + ); +} diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/getShiftAvailableMarkets.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/getShiftAvailableMarkets.tsx new file mode 100644 index 0000000000..7eb1131810 --- /dev/null +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/getShiftAvailableMarkets.tsx @@ -0,0 +1,36 @@ +import type { MarketInfo } from "domain/synthetics/markets/types"; +import { EMPTY_ARRAY } from "lib/objects"; + +export function getShiftAvailableMarkets({ + sortedMarketsInfoByIndexToken, +}: { + sortedMarketsInfoByIndexToken: MarketInfo[]; +}): MarketInfo[] { + if (sortedMarketsInfoByIndexToken.length === 0) { + return EMPTY_ARRAY; + } + + const shiftGroups: { [longShortKey: string]: MarketInfo[] } = {}; + + for (const marketInfo of sortedMarketsInfoByIndexToken) { + const longShortKey = `${marketInfo.longTokenAddress}-${marketInfo.shortTokenAddress}`; + + if (!shiftGroups[longShortKey]) { + shiftGroups[longShortKey] = []; + } + + shiftGroups[longShortKey].push(marketInfo); + } + + const availableMarkets: MarketInfo[] = []; + + for (const marketInfo of sortedMarketsInfoByIndexToken) { + const longShortKey = `${marketInfo.longTokenAddress}-${marketInfo.shortTokenAddress}`; + + if (shiftGroups[longShortKey].length > 1) { + availableMarkets.push(marketInfo); + } + } + + return availableMarkets; +} diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/getShiftAvailableRelatedMarkets.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/getShiftAvailableRelatedMarkets.tsx new file mode 100644 index 0000000000..fdef3a1abb --- /dev/null +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/getShiftAvailableRelatedMarkets.tsx @@ -0,0 +1,37 @@ +import { EMPTY_ARRAY } from "lib/objects"; +import type { MarketInfo, MarketsInfoData } from "domain/synthetics/markets/types"; + +export function getShiftAvailableRelatedMarkets({ + marketsInfoData, + sortedMarketsInfoByIndexToken, + marketTokenAddress, +}: { + marketsInfoData: MarketsInfoData | undefined; + sortedMarketsInfoByIndexToken: MarketInfo[]; + marketTokenAddress?: string; +}) { + if (!marketsInfoData) { + return EMPTY_ARRAY; + } + + if (!marketTokenAddress) { + return sortedMarketsInfoByIndexToken; + } + + const currentMarketInfo = marketsInfoData[marketTokenAddress]; + + if (!currentMarketInfo) { + return EMPTY_ARRAY; + } + + const longTokenAddress = currentMarketInfo.longTokenAddress; + const shortTokenAddress = currentMarketInfo.shortTokenAddress; + + return sortedMarketsInfoByIndexToken.filter((marketInfo) => { + const isSame = marketInfo.marketTokenAddress === marketTokenAddress; + const isRelated = + marketInfo.longTokenAddress === longTokenAddress && marketInfo.shortTokenAddress === shortTokenAddress; + + return !isSame && isRelated; + }); +} diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableMarkets.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableMarkets.tsx new file mode 100644 index 0000000000..d73bbf92a6 --- /dev/null +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableMarkets.tsx @@ -0,0 +1,17 @@ +import { useMemo } from "react"; + +import type { MarketInfo } from "domain/synthetics/markets/types"; + +import { getShiftAvailableMarkets } from "./getShiftAvailableMarkets"; + +export function useShiftAvailableMarkets(sortedMarketsInfoByIndexToken: MarketInfo[]) { + const shiftAvailableMarkets: MarketInfo[] = useMemo( + () => + getShiftAvailableMarkets({ + sortedMarketsInfoByIndexToken, + }), + [sortedMarketsInfoByIndexToken] + ); + + return shiftAvailableMarkets; +} diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableRelatedMarkets.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableRelatedMarkets.tsx new file mode 100644 index 0000000000..06d1cf589d --- /dev/null +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableRelatedMarkets.tsx @@ -0,0 +1,23 @@ +import { useMemo } from "react"; + +import type { MarketInfo, MarketsInfoData } from "domain/synthetics/markets/types"; + +import { getShiftAvailableRelatedMarkets } from "./getShiftAvailableRelatedMarkets"; + +export function useShiftAvailableRelatedMarkets( + marketsInfoData: MarketsInfoData | undefined, + sortedMarketsInfoByIndexToken: MarketInfo[], + marketTokenAddress?: string +) { + const shiftAvailableMarkets: MarketInfo[] = useMemo( + () => + getShiftAvailableRelatedMarkets({ + marketsInfoData, + sortedMarketsInfoByIndexToken, + marketTokenAddress, + }), + [marketTokenAddress, marketsInfoData, sortedMarketsInfoByIndexToken] + ); + + return shiftAvailableMarkets; +} diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useUpdateMarkets.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useUpdateMarkets.tsx new file mode 100644 index 0000000000..5ab10ae05c --- /dev/null +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useUpdateMarkets.tsx @@ -0,0 +1,83 @@ +import { Dispatch, SetStateAction, useEffect } from "react"; + +import type { MarketInfo, MarketsInfoData } from "domain/synthetics/markets/types"; +import { getByKey } from "lib/objects"; + +import { getShiftAvailableRelatedMarkets } from "./getShiftAvailableRelatedMarkets"; + +export function useUpdateMarkets({ + marketsInfoData, + selectedMarketAddress, + shiftAvailableMarkets, + onSelectMarket, + toMarketAddress, + toMarketInfo, + selectedMarketInfo, + setToMarketAddress, +}: { + marketsInfoData: MarketsInfoData | undefined; + selectedMarketAddress: string | undefined; + shiftAvailableMarkets: MarketInfo[]; + onSelectMarket: (marketAddress: string) => void; + toMarketAddress: string | undefined; + toMarketInfo: MarketInfo | undefined; + selectedMarketInfo: MarketInfo | undefined; + setToMarketAddress: Dispatch>; +}): void { + useEffect( + function updateMarkets() { + if (!marketsInfoData) { + return; + } + + let newSelectedMarketAddress = selectedMarketAddress; + + const isSelectedMarketValid = Boolean( + selectedMarketAddress && + getByKey(marketsInfoData, selectedMarketAddress) && + shiftAvailableMarkets.find((market) => market.marketTokenAddress === selectedMarketAddress) + ); + + if (!isSelectedMarketValid) { + const firstMarketInfo = shiftAvailableMarkets[0]; + if (!firstMarketInfo) { + return; + } + onSelectMarket(firstMarketInfo.marketTokenAddress); + newSelectedMarketAddress = firstMarketInfo.marketTokenAddress; + } + + const isToMarketAvailable = Boolean(toMarketAddress && getByKey(marketsInfoData, toMarketAddress)); + const isToMarketRelated = + toMarketInfo?.longTokenAddress === selectedMarketInfo?.longTokenAddress && + toMarketInfo?.shortTokenAddress === selectedMarketInfo?.shortTokenAddress; + const isToMarketValid = isToMarketAvailable && isToMarketRelated; + + if (!isToMarketValid) { + const someAvailableMarket = getShiftAvailableRelatedMarkets({ + marketsInfoData, + sortedMarketsInfoByIndexToken: shiftAvailableMarkets, + marketTokenAddress: newSelectedMarketAddress, + })[0]; + + if (!someAvailableMarket) { + return; + } + + setToMarketAddress(someAvailableMarket.marketTokenAddress); + } + }, + [ + marketsInfoData, + onSelectMarket, + selectedMarketAddress, + selectedMarketInfo?.longTokenAddress, + selectedMarketInfo?.shortTokenAddress, + setToMarketAddress, + shiftAvailableMarkets, + toMarketAddress, + toMarketInfo?.longTokenAddress, + toMarketInfo?.shortTokenAddress, + ] + ); +} diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx index 4cd09d5048..e374f6989e 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx @@ -1,82 +1,19 @@ -import { msg, t } from "@lingui/macro"; -import { useConnectModal } from "@rainbow-me/rainbowkit"; -import cx from "classnames"; -import { Dispatch, SetStateAction, useCallback, useEffect, useMemo } from "react"; +import { msg } from "@lingui/macro"; +import { Dispatch, SetStateAction, useCallback } from "react"; -import { HIGH_PRICE_IMPACT_BPS } from "config/factors"; -import { NATIVE_TOKEN_ADDRESS } from "config/tokens"; -import { MAX_METAMASK_MOBILE_DECIMALS } from "config/ui"; -import { useSettings } from "context/SettingsContext/SettingsContextProvider"; -import { useMarketsInfoData, useTokensData } from "context/SyntheticsStateContext/hooks/globalsHooks"; -import { useHasOutdatedUi } from "domain/legacy"; -import { - FeeItem, - estimateExecuteDepositGasLimit, - estimateExecuteWithdrawalGasLimit, - getExecutionFee, - getFeeItem, - getTotalFeeItem, - useGasLimits, - useGasPrice, -} from "domain/synthetics/fees"; -import { - estimateDepositOraclePriceCount, - estimateWithdrawalOraclePriceCount, -} from "domain/synthetics/fees/utils/estimateOraclePriceCount"; -import useUiFeeFactor from "domain/synthetics/fees/utils/useUiFeeFactor"; -import { useMarketTokensData } from "domain/synthetics/markets"; -import { MarketInfo } from "domain/synthetics/markets/types"; -import { - getAvailableUsdLiquidityForCollateral, - getMarketIndexName, - getTokenPoolType, -} from "domain/synthetics/markets/utils"; -import { TokenData, convertToUsd, getTokenData } from "domain/synthetics/tokens"; -import { useGmTokensFavorites } from "domain/synthetics/tokens/useGmTokensFavorites"; -import { GmSwapFees, useAvailableTokenOptions } from "domain/synthetics/trade"; -import useSortedPoolsWithIndexToken from "domain/synthetics/trade/useSortedPoolsWithIndexToken"; -import { getCommonError, getGmSwapError } from "domain/synthetics/trade/utils/validation"; -import { Token, getMinResidualAmount } from "domain/tokens"; -import { bigMath } from "lib/bigmath"; -import { useChainId } from "lib/chains"; +import { useMarketsInfoData } from "context/SyntheticsStateContext/hooks/globalsHooks"; import { useLocalizedMap } from "lib/i18n"; -import { formatAmountFree, formatTokenAmount, formatUsd, limitDecimals, parseValue } from "lib/numbers"; import { getByKey } from "lib/objects"; -import useIsMetamaskMobile from "lib/wallets/useIsMetamaskMobile"; -import useWallet from "lib/wallets/useWallet"; - import { getGmSwapBoxAvailableModes } from "./getGmSwapBoxAvailableModes"; -import { showMarketToast } from "./showMarketToast"; -import { useDepositWithdrawalAmounts } from "./useDepositWithdrawalAmounts"; -import { useGmSwapBoxState } from "./useGmSwapBoxState"; -import { useUpdateByQueryParams } from "./useUpdateByQueryParams"; -import { useUpdateInputAmounts } from "./useUpdateInputAmounts"; -import { useUpdateTokens } from "./useUpdateTokens"; +import { Mode, Operation } from "./types"; -import Button from "components/Button/Button"; -import BuyInputSection from "components/BuyInputSection/BuyInputSection"; -import { PoolSelector } from "components/MarketSelector/PoolSelector"; import Tab from "components/Tab/Tab"; -import TokenWithIcon from "components/TokenIcon/TokenWithIcon"; -import TokenSelector from "components/TokenSelector/TokenSelector"; -import { GmConfirmationBox } from "../GmConfirmationBox/GmConfirmationBox"; -import { InfoRows } from "./InfoRows"; -import { Swap } from "./Swap"; +import { GmShiftBox } from "./GmShiftBox/GmShiftBox"; +import { GmSwapBoxDepositWithdrawal } from "./GmDepositWithdrawalBox/GmDepositWithdrawalBox"; import "./GmSwapBox.scss"; -export enum Operation { - Deposit = "Deposit", - Withdrawal = "Withdrawal", - Shift = "Shift", -} - -export enum Mode { - Single = "Single", - Pair = "Pair", -} - -type Props = { +export type GmSwapBoxProps = { selectedMarketAddress?: string; onSelectMarket: (marketAddress: string) => void; operation: Operation; @@ -88,6 +25,7 @@ type Props = { const OPERATION_LABELS = { [Operation.Deposit]: msg`Buy GM`, [Operation.Withdrawal]: msg`Sell GM`, + [Operation.Shift]: msg`Shift GM`, }; const MODE_LABELS = { @@ -95,622 +33,26 @@ const MODE_LABELS = { [Mode.Pair]: msg`Pair`, }; -export function GmSwapBox(p: Props) { +export function GmSwapBox(p: GmSwapBoxProps) { const { selectedMarketAddress, operation, mode, onSetMode, onSetOperation, onSelectMarket } = p; - const isMetamaskMobile = useIsMetamaskMobile(); - const { openConnectModal } = useConnectModal(); const marketAddress = selectedMarketAddress; - const { shouldDisableValidationForTesting } = useSettings(); - - const { chainId } = useChainId(); - const { account } = useWallet(); - // #region Requests - const { marketTokensData: depositMarketTokensData } = useMarketTokensData(chainId, { isDeposit: true }); - const { marketTokensData: withdrawalMarketTokensData } = useMarketTokensData(chainId, { isDeposit: false }); - const gasLimits = useGasLimits(chainId); - const gasPrice = useGasPrice(chainId); - const uiFeeFactor = useUiFeeFactor(chainId); - const { data: hasOutdatedUi } = useHasOutdatedUi(); - // #endregion - - // #region Selectors const marketsInfoData = useMarketsInfoData(); - const { marketsInfo: sortedMarketsInfoByIndexToken } = useSortedPoolsWithIndexToken( - marketsInfoData, - depositMarketTokensData - ); - const tokensData = useTokensData(); - const { infoTokens } = useAvailableTokenOptions(chainId, { marketsInfoData, tokensData }); - // #endregion - - // #region State - const gmTokenFavoritesContext = useGmTokensFavorites(); - const { - focusedInput, - setFocusedInput, - - stage, - setStage, - - isHighPriceImpactAccepted, - setIsHighPriceImpactAccepted, - - indexName, - setIndexName, - - firstTokenAddress, - setFirstTokenAddress, - - secondTokenAddress, - setSecondTokenAddress, - - firstTokenInputValue, - setFirstTokenInputValue, - - secondTokenInputValue, - setSecondTokenInputValue, - - marketTokenInputValue, - setMarketTokenInputValue, - } = useGmSwapBoxState(operation, mode, marketAddress); - // #endregion - - // #region Derived state - const nativeToken = getByKey(tokensData, NATIVE_TOKEN_ADDRESS); - const minResidualAmount = getMinResidualAmount(nativeToken?.decimals, nativeToken?.prices?.maxPrice); - - const isDeposit = operation === Operation.Deposit; - const isWithdrawal = operation === Operation.Withdrawal; - const isShift = operation === Operation.Shift; - const isSingle = mode === Mode.Single; - const isPair = mode === Mode.Pair; - - const marketTokensData = isDeposit ? depositMarketTokensData : withdrawalMarketTokensData; const marketInfo = getByKey(marketsInfoData, marketAddress); const availableModes = getGmSwapBoxAvailableModes(operation, marketInfo); - const firstToken = getTokenData(tokensData, firstTokenAddress); - let firstTokenAmount = parseValue(firstTokenInputValue, firstToken?.decimals || 0); - const firstTokenUsd = convertToUsd( - firstTokenAmount, - firstToken?.decimals, - isDeposit ? firstToken?.prices?.minPrice : firstToken?.prices?.maxPrice - ); - - const secondToken = getTokenData(tokensData, secondTokenAddress); - let secondTokenAmount = parseValue(secondTokenInputValue, secondToken?.decimals || 0); - const secondTokenUsd = convertToUsd( - secondTokenAmount, - secondToken?.decimals, - isDeposit ? secondToken?.prices?.minPrice : secondToken?.prices?.maxPrice - ); - - const { - longTokenInputState, - // Undefined when isSameCollaterals is true - shortTokenInputState, - } = useMemo(() => { - if (!marketInfo) { - return {}; - } - - const inputs: { - address: string; - value: string; - amount?: bigint; - usd?: bigint; - token?: TokenData; - setValue: (val: string) => void; - }[] = []; - - if (firstTokenAddress) { - inputs.push({ - address: firstTokenAddress, - value: firstTokenInputValue, - setValue: setFirstTokenInputValue, - amount: firstTokenAmount, - usd: firstTokenUsd, - token: firstToken, - }); - } - - if (isPair && secondTokenAddress) { - inputs.push({ - address: secondTokenAddress, - value: secondTokenInputValue, - setValue: setSecondTokenInputValue, - amount: secondTokenAmount, - usd: secondTokenUsd, - token: secondToken, - }); - } - - const longTokenInputState = inputs.find((input) => getTokenPoolType(marketInfo, input.address) === "long"); - const shortTokenInputState = inputs.find((input) => getTokenPoolType(marketInfo, input.address) === "short"); - - return { - longTokenInputState, - shortTokenInputState, - }; - }, [ - firstToken, - firstTokenAddress, - firstTokenAmount, - firstTokenInputValue, - firstTokenUsd, - isPair, - marketInfo, - secondToken, - secondTokenAddress, - secondTokenAmount, - secondTokenInputValue, - secondTokenUsd, - setFirstTokenInputValue, - setSecondTokenInputValue, - ]); - - const tokenOptions: Token[] = useMemo( - function getTokenOptions() { - const { longToken, shortToken } = marketInfo || {}; - - if (!longToken || !shortToken) return []; - - const result = [longToken]; - - if (longToken.address !== shortToken.address) { - result.push(shortToken); - } - - const nativeToken = getByKey(tokensData, NATIVE_TOKEN_ADDRESS)!; - - if (result.some((token) => token.isWrapped) && nativeToken) { - result.unshift(nativeToken); - } - - return result; - }, - [marketInfo, tokensData] - ); - - const marketToken = getTokenData( - isDeposit ? depositMarketTokensData : withdrawalMarketTokensData, - marketInfo?.marketTokenAddress - ); - const marketTokenAmount = parseValue(marketTokenInputValue || "0", marketToken?.decimals || 0)!; - const marketTokenUsd = convertToUsd( - marketTokenAmount, - marketToken?.decimals, - isDeposit ? marketToken?.prices?.maxPrice : marketToken?.prices?.minPrice - )!; - - const { longCollateralLiquidityUsd, shortCollateralLiquidityUsd } = useMemo(() => { - if (!marketInfo) return {}; - - return { - longCollateralLiquidityUsd: getAvailableUsdLiquidityForCollateral(marketInfo, true), - shortCollateralLiquidityUsd: getAvailableUsdLiquidityForCollateral(marketInfo, false), - }; - }, [marketInfo]); - - const amounts = useDepositWithdrawalAmounts({ - isDeposit, - marketInfo, - marketToken, - longTokenInputState, - shortTokenInputState, - marketTokenAmount, - uiFeeFactor, - focusedInput, - isWithdrawal, - }); - - const { fees, executionFee } = useMemo(() => { - if (!gasLimits || gasPrice === undefined || !tokensData || !amounts) { - return {}; - } - - const basisUsd = isDeposit - ? (amounts?.longTokenUsd ?? 0n) + (amounts?.shortTokenUsd ?? 0n) - : amounts?.marketTokenUsd || 0n; - - const swapFee = getFeeItem(amounts.swapFeeUsd * -1n, basisUsd); - const swapPriceImpact = getFeeItem(amounts.swapPriceImpactDeltaUsd, basisUsd); - const uiFee = getFeeItem(amounts.uiFeeUsd * -1n, basisUsd, { - shouldRoundUp: true, - }); - - const totalFees = getTotalFeeItem([swapPriceImpact, swapFee, uiFee].filter(Boolean) as FeeItem[]); - const fees: GmSwapFees = { - swapFee, - swapPriceImpact, - totalFees, - uiFee, - }; - - const gasLimit = isDeposit - ? estimateExecuteDepositGasLimit(gasLimits, { - initialLongTokenAmount: amounts.longTokenAmount, - initialShortTokenAmount: amounts.shortTokenAmount, - }) - : estimateExecuteWithdrawalGasLimit(gasLimits, {}); - - const oraclePriceCount = isDeposit ? estimateDepositOraclePriceCount(0) : estimateWithdrawalOraclePriceCount(0); - - const executionFee = getExecutionFee(chainId, gasLimits, tokensData, gasLimit, gasPrice, oraclePriceCount); - - return { - fees, - executionFee, - }; - }, [amounts, chainId, gasLimits, gasPrice, isDeposit, tokensData]); - - const isHighPriceImpact = - (fees?.swapPriceImpact?.deltaUsd ?? 0) < 0 && - bigMath.abs(fees?.swapPriceImpact?.bps ?? 0n) >= HIGH_PRICE_IMPACT_BPS; - - const submitState = useMemo(() => { - const commonError = getCommonError({ - chainId, - isConnected: true, - hasOutdatedUi, - })[0]; - - const swapError = getGmSwapError({ - isDeposit, - marketInfo, - marketToken, - longToken: longTokenInputState?.token, - shortToken: shortTokenInputState?.token, - marketTokenAmount, - marketTokenUsd: amounts?.marketTokenUsd, - longTokenAmount: amounts?.longTokenAmount, - shortTokenAmount: amounts?.shortTokenAmount, - longTokenUsd: amounts?.longTokenUsd, - shortTokenUsd: amounts?.shortTokenUsd, - longTokenLiquidityUsd: longCollateralLiquidityUsd, - shortTokenLiquidityUsd: shortCollateralLiquidityUsd, - fees, - isHighPriceImpact: Boolean(isHighPriceImpact), - isHighPriceImpactAccepted, - priceImpactUsd: fees?.swapPriceImpact?.deltaUsd, - })[0]; - - const error = commonError || swapError; - - if (!account) { - return { - text: t`Connect Wallet`, - onSubmit: () => openConnectModal?.(), - }; - } - - const onSubmit = () => { - setStage("confirmation"); - }; - - if (error) { - return { - text: error, - error, - isDisabled: !shouldDisableValidationForTesting, - onSubmit, - }; - } - - return { - text: isDeposit ? t`Buy GM` : t`Sell GM`, - onSubmit, - }; - }, [ - chainId, - hasOutdatedUi, - isDeposit, - marketInfo, - marketToken, - longTokenInputState?.token, - shortTokenInputState?.token, - marketTokenAmount, - amounts?.marketTokenUsd, - amounts?.longTokenAmount, - amounts?.shortTokenAmount, - amounts?.longTokenUsd, - amounts?.shortTokenUsd, - longCollateralLiquidityUsd, - shortCollateralLiquidityUsd, - fees, - isHighPriceImpact, - isHighPriceImpactAccepted, - account, - openConnectModal, - setStage, - shouldDisableValidationForTesting, - ]); - - const firstTokenShowMaxButton = - (isDeposit && - firstToken?.balance && - (firstTokenAmount === undefined || firstTokenAmount !== firstToken.balance) && - (firstToken?.isNative - ? minResidualAmount !== undefined && firstToken?.balance !== undefined && firstToken.balance > minResidualAmount - : true)) || - false; - - const secondTokenShowMaxButton = - (isDeposit && - secondToken?.balance && - (secondTokenAmount === undefined || secondTokenAmount !== secondToken.balance) && - (secondToken?.isNative - ? minResidualAmount !== undefined && - secondToken?.balance !== undefined && - secondToken.balance > minResidualAmount - : true)) || - false; - - const marketTokenInputShowMaxButton = - (isWithdrawal && - marketToken?.balance && - (marketTokenAmount === undefined || marketTokenAmount !== marketToken.balance)) || - false; - // #endregion - - // #region Callbacks - const onFocusedCollateralInputChange = useCallback( - (tokenAddress: string) => { - if (!marketInfo) { - return; - } - - if (marketInfo.isSameCollaterals) { - setFocusedInput("longCollateral"); - return; - } - - if (getTokenPoolType(marketInfo, tokenAddress) === "long") { - setFocusedInput("longCollateral"); - } else { - setFocusedInput("shortCollateral"); - } - }, - [marketInfo, setFocusedInput] - ); - - const resetInputs = useCallback(() => { - setFirstTokenInputValue(""); - setSecondTokenInputValue(""); - setMarketTokenInputValue(""); - }, [setFirstTokenInputValue, setMarketTokenInputValue, setSecondTokenInputValue]); - - const onSwitchSide = useCallback(() => { - setFocusedInput("market"); - resetInputs(); - onSetOperation(operation === Operation.Deposit ? Operation.Withdrawal : Operation.Deposit); - }, [operation, resetInputs, setFocusedInput, onSetOperation]); - const onOperationChange = useCallback( (operation: Operation) => { - resetInputs(); onSetOperation(operation); }, - [resetInputs, onSetOperation] + [onSetOperation] ); - const onMarketChange = useCallback( - (marketAddress: string) => { - resetInputs(); - onSelectMarket(marketAddress); - }, - [onSelectMarket, resetInputs] - ); - - const onMaxClickFirstToken = useCallback(() => { - if (firstToken?.balance) { - let maxAvailableAmount = firstToken.isNative - ? firstToken.balance - (minResidualAmount ?? 0n) - : firstToken.balance; - - if (maxAvailableAmount < 0) { - maxAvailableAmount = 0n; - } - - const formattedMaxAvailableAmount = formatAmountFree(maxAvailableAmount, firstToken.decimals); - const finalAmount = isMetamaskMobile - ? limitDecimals(formattedMaxAvailableAmount, MAX_METAMASK_MOBILE_DECIMALS) - : formattedMaxAvailableAmount; - - setFirstTokenInputValue(finalAmount); - onFocusedCollateralInputChange(firstToken.address); - } - }, [ - firstToken?.address, - firstToken?.balance, - firstToken?.decimals, - firstToken?.isNative, - isMetamaskMobile, - minResidualAmount, - onFocusedCollateralInputChange, - setFirstTokenInputValue, - ]); - - const onMaxClickSecondToken = useCallback(() => { - if (!isDeposit) { - return; - } - - if (secondToken?.balance === undefined) { - return; - } - - let maxAvailableAmount = secondToken.isNative - ? secondToken.balance - (minResidualAmount ?? 0n) - : secondToken.balance; - - if (maxAvailableAmount < 0) { - maxAvailableAmount = 0n; - } - - const formattedMaxAvailableAmount = formatAmountFree(maxAvailableAmount, secondToken.decimals); - const finalAmount = isMetamaskMobile - ? limitDecimals(formattedMaxAvailableAmount, MAX_METAMASK_MOBILE_DECIMALS) - : formattedMaxAvailableAmount; - setSecondTokenInputValue(finalAmount); - onFocusedCollateralInputChange(secondToken.address); - }, [ - isDeposit, - isMetamaskMobile, - minResidualAmount, - onFocusedCollateralInputChange, - secondToken?.address, - secondToken?.balance, - secondToken?.decimals, - secondToken?.isNative, - setSecondTokenInputValue, - ]); - const localizedOperationLabels = useLocalizedMap(OPERATION_LABELS); const localizedModeLabels = useLocalizedMap(MODE_LABELS); - const handleFirstTokenInputValueChange = useCallback( - (e) => { - if (firstToken) { - setFirstTokenInputValue(e.target.value); - onFocusedCollateralInputChange(firstToken.address); - } - }, - [firstToken, onFocusedCollateralInputChange, setFirstTokenInputValue] - ); - - const handleFormSubmit = useCallback( - (e: React.FormEvent) => { - e.preventDefault(); - submitState.onSubmit(); - }, - [submitState] - ); - - const marketTokenInputClickMax = useCallback(() => { - if (marketToken?.balance) { - const formattedGMBalance = formatAmountFree(marketToken.balance, marketToken.decimals); - const finalGMBalance = isMetamaskMobile - ? limitDecimals(formattedGMBalance, MAX_METAMASK_MOBILE_DECIMALS) - : formattedGMBalance; - setMarketTokenInputValue(finalGMBalance); - setFocusedInput("market"); - } - }, [isMetamaskMobile, marketToken?.balance, marketToken?.decimals, setFocusedInput, setMarketTokenInputValue]); - - const marketTokenInputClickTopRightLabel = useCallback(() => { - if (!isWithdrawal) { - return; - } - - if (marketToken?.balance) { - setMarketTokenInputValue(formatAmountFree(marketToken.balance, marketToken.decimals)); - setFocusedInput("market"); - } - }, [isWithdrawal, marketToken?.balance, marketToken?.decimals, setFocusedInput, setMarketTokenInputValue]); - - const marketTokenInputValueChange = useCallback( - (e: React.ChangeEvent) => { - setMarketTokenInputValue(e.target.value); - setFocusedInput("market"); - }, - [setFocusedInput, setMarketTokenInputValue] - ); - - const secondTokenInputValueChange = useCallback( - (e: React.ChangeEvent) => { - if (secondToken) { - setSecondTokenInputValue(e.target.value); - onFocusedCollateralInputChange(secondToken.address); - } - }, - [onFocusedCollateralInputChange, secondToken, setSecondTokenInputValue] - ); - - const firstTokenSelectToken = useCallback( - (token: Token): void => setFirstTokenAddress(token.address), - [setFirstTokenAddress] - ); - - const marketTokenSelectMarket = useCallback( - (marketInfo: MarketInfo): void => { - setIndexName(getMarketIndexName(marketInfo)); - onMarketChange(marketInfo.marketTokenAddress); - showMarketToast(marketInfo); - }, - [onMarketChange, setIndexName] - ); - // #endregion - - // #region Effects - useUpdateInputAmounts({ - marketToken, - marketInfo, - longTokenInputState, - shortTokenInputState, - isDeposit, - focusedInput, - amounts, - setMarketTokenInputValue, - marketTokenAmount, - isWithdrawal, - setFirstTokenInputValue, - setSecondTokenInputValue, - }); - - useEffect( - function updateIndexToken() { - if (!indexName && sortedMarketsInfoByIndexToken.length) { - setIndexName(getMarketIndexName(sortedMarketsInfoByIndexToken[0])); - } - }, - [indexName, sortedMarketsInfoByIndexToken, setIndexName] - ); - - useEffect( - function updateMarket() { - const marketsByIndexName = sortedMarketsInfoByIndexToken.filter( - (market) => getMarketIndexName(market) === indexName - ); - - if (!marketsByIndexName.length) { - return; - } - - if (!marketAddress || !marketsByIndexName.find((market) => market.marketTokenAddress === marketAddress)) { - onMarketChange(marketsByIndexName[0].marketTokenAddress); - } - }, - [indexName, marketAddress, sortedMarketsInfoByIndexToken, onMarketChange] - ); - - useUpdateByQueryParams({ - setOperation: onSetOperation, - setMode: onSetMode, - setIndexName, - onSelectMarket, - setFirstTokenAddress, - }); - - useUpdateTokens({ - tokenOptions, - firstTokenAddress, - setFirstTokenAddress, - isSingle, - secondTokenAddress, - marketInfo, - secondTokenAmount, - setFocusedInput, - setSecondTokenAddress, - setSecondTokenInputValue, - isPair, - chainId, - }); - // #endregion - return (
-
-
- - {firstTokenAddress && isSingle ? ( - - ) : ( -
- -
- )} -
- - {isPair && secondTokenAddress && ( - -
- -
-
- )} - - - - - - -
- - - -
- -
- - - { - setStage("swap"); - }} - onClose={() => { - setStage("swap"); - }} - shouldDisableValidation={shouldDisableValidationForTesting} - /> + ) : ( + + )}
); } diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/getGmSwapBoxAvailableModes.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/getGmSwapBoxAvailableModes.tsx index ab2899c786..d9fa2a5295 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/getGmSwapBoxAvailableModes.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/getGmSwapBoxAvailableModes.tsx @@ -1,5 +1,5 @@ import { Market } from "domain/synthetics/markets/types"; -import { Mode, Operation } from "./GmSwapBox"; +import { Mode, Operation } from "./types"; export const getGmSwapBoxAvailableModes = ( operation: Operation, diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/types.ts b/src/components/Synthetics/GmSwap/GmSwapBox/types.ts new file mode 100644 index 0000000000..7187018d61 --- /dev/null +++ b/src/components/Synthetics/GmSwap/GmSwapBox/types.ts @@ -0,0 +1,10 @@ +export enum Operation { + Deposit = "Deposit", + Withdrawal = "Withdrawal", + Shift = "Shift", +} + +export enum Mode { + Single = "Single", + Pair = "Pair", +} diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/useUpdateInputAmounts.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/useUpdateInputAmounts.tsx index 46e7ce12f0..66f177770d 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/useUpdateInputAmounts.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/useUpdateInputAmounts.tsx @@ -1,6 +1,8 @@ import { Dispatch, SetStateAction, useEffect } from "react"; + import { MarketInfo } from "domain/synthetics/markets/types"; import { TokenData } from "domain/synthetics/tokens"; +import type { DepositAmounts, WithdrawalAmounts } from "domain/synthetics/trade"; import { formatAmountFree } from "lib/numbers"; export function useUpdateInputAmounts({ @@ -41,7 +43,7 @@ export function useUpdateInputAmounts({ | undefined; isDeposit: boolean; focusedInput: string; - amounts: import("/root/dev/midas/gmx-interface/src/domain/synthetics/trade/types").DepositAmounts | undefined; + amounts: DepositAmounts | WithdrawalAmounts | undefined; setMarketTokenInputValue: Dispatch>; marketTokenAmount: bigint; isWithdrawal: boolean; diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/useUpdateTokens.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/useUpdateTokens.tsx index 57585a8c16..c5aa418562 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/useUpdateTokens.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/useUpdateTokens.tsx @@ -1,35 +1,36 @@ import { Dispatch, SetStateAction, useEffect } from "react"; + import { convertTokenAddress } from "config/tokens"; import { MarketInfo } from "domain/synthetics/markets/types"; import { getTokenPoolType } from "domain/synthetics/markets/utils"; import { Token } from "domain/tokens"; export function useUpdateTokens({ + chainId, tokenOptions, firstTokenAddress, setFirstTokenAddress, isSingle, + isPair, secondTokenAddress, marketInfo, secondTokenAmount, setFocusedInput, setSecondTokenAddress, setSecondTokenInputValue, - isPair, - chainId, }: { + chainId: number; tokenOptions: Token[]; firstTokenAddress: string | undefined; setFirstTokenAddress: Dispatch>; isSingle: boolean; + isPair: boolean; secondTokenAddress: string | undefined; marketInfo: MarketInfo | undefined; secondTokenAmount: bigint | undefined; setFocusedInput: Dispatch>; setSecondTokenAddress: Dispatch>; setSecondTokenInputValue: Dispatch>; - isPair: boolean; - chainId: number; }) { useEffect( function updateTokens() { diff --git a/src/components/Synthetics/StatusNotification/GmStatusNotification.tsx b/src/components/Synthetics/StatusNotification/GmStatusNotification.tsx index 5709b69ca1..c048ff49fb 100644 --- a/src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +++ b/src/components/Synthetics/StatusNotification/GmStatusNotification.tsx @@ -3,8 +3,10 @@ import { TransactionStatus, TransactionStatusType } from "components/Transaction import { convertTokenAddress } from "config/tokens"; import { PendingDepositData, + PendingShiftData, PendingWithdrawalData, getPendingDepositKey, + getPendingShiftKey, getPendingWithdrawalKey, useSyntheticsEvents, } from "context/SyntheticsEvents"; @@ -20,32 +22,75 @@ export type Props = { toastTimestamp: number; pendingDepositData?: PendingDepositData; pendingWithdrawalData?: PendingWithdrawalData; + pendingShiftData?: PendingShiftData; marketsInfoData?: MarketsInfoData; tokensData?: TokensData; }; +function select( + deposit: A, + withdrawal: B, + shift: C, + operation: "deposit" | "withdrawal" | "shift" +): A | B | C { + if (operation === "deposit") { + return deposit; + } else if (operation === "withdrawal") { + return withdrawal; + } else { + return shift; + } +} + export function GmStatusNotification({ toastTimestamp, pendingDepositData, pendingWithdrawalData, + pendingShiftData, marketsInfoData, tokensData, }: Props) { const { chainId } = useChainId(); - const { depositStatuses, withdrawalStatuses, setDepositStatusViewed, setWithdrawalStatusViewed } = - useSyntheticsEvents(); - - const isDeposit = Boolean(pendingDepositData); + const { + depositStatuses, + withdrawalStatuses, + shiftStatuses, + // + setDepositStatusViewed, + setWithdrawalStatusViewed, + setShiftStatusViewed, + } = useSyntheticsEvents(); + + let operation: "deposit" | "withdrawal" | "shift"; + if (pendingDepositData) { + operation = "deposit"; + } else if (pendingWithdrawalData) { + operation = "withdrawal"; + } else { + operation = "shift"; + } const [depositStatusKey, setDepositStatusKey] = useState(); const [withdrawalStatusKey, setWithdrawalStatusKey] = useState(); + const [shiftStatusKey, setShiftStatusKey] = useState(); const depositStatus = getByKey(depositStatuses, depositStatusKey); const withdrawalStatus = getByKey(withdrawalStatuses, withdrawalStatusKey); + const shiftStatus = getByKey(shiftStatuses, shiftStatusKey); - const isCompleted = isDeposit ? Boolean(depositStatus?.executedTxnHash) : Boolean(withdrawalStatus?.executedTxnHash); + const isCompleted = select( + Boolean(depositStatus?.executedTxnHash), + Boolean(withdrawalStatus?.executedTxnHash), + Boolean(shiftStatus?.executedTxnHash), + operation + ); - const hasError = isDeposit ? Boolean(depositStatus?.cancelledTxnHash) : Boolean(withdrawalStatus?.cancelledTxnHash); + const hasError = select( + Boolean(depositStatus?.cancelledTxnHash), + Boolean(withdrawalStatus?.cancelledTxnHash), + Boolean(shiftStatus?.cancelledTxnHash), + operation + ); const pendingDepositKey = useMemo(() => { if (pendingDepositData) { @@ -59,8 +104,14 @@ export function GmStatusNotification({ } }, [pendingWithdrawalData]); + const pendingShiftKey = useMemo(() => { + if (pendingShiftData) { + return getPendingShiftKey(pendingShiftData); + } + }, [pendingShiftData]); + const title = useMemo(() => { - if (isDeposit) { + if (operation === "deposit") { if (!pendingDepositData) { return t`Unknown buy GM order`; } @@ -113,7 +164,7 @@ export function GmStatusNotification({ with {tokensText} ); - } else { + } else if (operation === "withdrawal") { if (!pendingWithdrawalData) { return t`Unknown sell GM order`; } @@ -130,15 +181,28 @@ export function GmStatusNotification({
); + } else { + if (!pendingShiftData) { + return t`Unknown shift GM order`; + } + + // const fromToken = getByKey(tokensData, pendingShiftData.fromMarketTokenAddress); + // const toToken = getByKey(tokensData, pendingShiftData.toMarketTokenAddress); + + return ( + +
Shifting GM
+
+ ); } - }, [chainId, isDeposit, marketsInfoData, pendingDepositData, pendingWithdrawalData, tokensData]); + }, [chainId, marketsInfoData, operation, pendingDepositData, pendingShiftData, pendingWithdrawalData, tokensData]); const creationStatus = useMemo(() => { let text = ""; let status: TransactionStatusType = "loading"; let createdTxnHash: string | undefined; - if (isDeposit) { + if (operation === "deposit") { text = t`Sending Buy request`; if (depositStatus?.createdTxnHash) { @@ -146,7 +210,7 @@ export function GmStatusNotification({ status = "success"; createdTxnHash = depositStatus?.createdTxnHash; } - } else { + } else if (operation === "withdrawal") { text = t`Sending Sell request`; if (withdrawalStatus?.createdTxnHash) { @@ -154,17 +218,25 @@ export function GmStatusNotification({ status = "success"; createdTxnHash = withdrawalStatus?.createdTxnHash; } + } else { + text = t`Sending Shift request`; + + if (shiftStatus?.createdTxnHash) { + text = t`Shift request sent`; + status = "success"; + createdTxnHash = shiftStatus?.createdTxnHash; + } } return ; - }, [depositStatus?.createdTxnHash, isDeposit, withdrawalStatus?.createdTxnHash]); + }, [depositStatus?.createdTxnHash, operation, shiftStatus?.createdTxnHash, withdrawalStatus?.createdTxnHash]); const executionStatus = useMemo(() => { let text = ""; let status: TransactionStatusType = "muted"; let txnHash: string | undefined; - if (isDeposit) { + if (operation === "deposit") { text = t`Fulfilling Buy request`; if (depositStatus?.createdTxnHash) { @@ -182,7 +254,7 @@ export function GmStatusNotification({ status = "error"; txnHash = depositStatus?.cancelledTxnHash; } - } else { + } else if (operation === "withdrawal") { text = t`Fulfilling Sell request`; if (withdrawalStatus?.createdTxnHash) { @@ -200,14 +272,43 @@ export function GmStatusNotification({ status = "error"; txnHash = withdrawalStatus?.cancelledTxnHash; } + } else { + text = t`Fulfilling Shift request`; + + if (shiftStatus?.createdTxnHash) { + status = "loading"; + } + + if (shiftStatus?.executedTxnHash) { + text = t`Shift order executed`; + status = "success"; + txnHash = shiftStatus?.executedTxnHash; + } + + if (shiftStatus?.cancelledTxnHash) { + text = t`Shift order cancelled`; + status = "error"; + txnHash = shiftStatus?.cancelledTxnHash; + } } return ; - }, [depositStatus, isDeposit, withdrawalStatus]); + }, [ + depositStatus?.cancelledTxnHash, + depositStatus?.createdTxnHash, + depositStatus?.executedTxnHash, + operation, + shiftStatus?.cancelledTxnHash, + shiftStatus?.createdTxnHash, + shiftStatus?.executedTxnHash, + withdrawalStatus?.cancelledTxnHash, + withdrawalStatus?.createdTxnHash, + withdrawalStatus?.executedTxnHash, + ]); useEffect( function getStatusKey() { - if (isDeposit) { + if (operation === "deposit") { if (depositStatusKey) { return; } @@ -220,7 +321,7 @@ export function GmStatusNotification({ setDepositStatusKey(matchedStatusKey); setDepositStatusViewed(matchedStatusKey); } - } else { + } else if (operation === "withdrawal") { if (withdrawalStatusKey) { return; } @@ -233,16 +334,33 @@ export function GmStatusNotification({ setWithdrawalStatusKey(matchedStatusKey); setWithdrawalStatusViewed(matchedStatusKey); } + } else { + if (shiftStatusKey) { + return; + } + + const matchedStatusKey = Object.values(shiftStatuses).find( + (status) => !status.isViewed && status.data && getPendingShiftKey(status.data) === pendingShiftKey + )?.key; + + if (matchedStatusKey) { + setShiftStatusKey(matchedStatusKey); + setShiftStatusViewed(matchedStatusKey); + } } }, [ depositStatusKey, depositStatuses, - isDeposit, + operation, pendingDepositKey, + pendingShiftKey, pendingWithdrawalKey, setDepositStatusViewed, + setShiftStatusViewed, setWithdrawalStatusViewed, + shiftStatusKey, + shiftStatuses, toastTimestamp, withdrawalStatusKey, withdrawalStatuses, diff --git a/src/config/contracts.ts b/src/config/contracts.ts index 73d0d03873..55cc12edf9 100644 --- a/src/config/contracts.ts +++ b/src/config/contracts.ts @@ -186,6 +186,7 @@ const CONTRACTS = { DepositVault: "0xF89e77e8Dc11691C9e8757e84aaFbCD8A67d7A55", WithdrawalVault: "0x0628D46b5D145f183AdB6Ef1f2c97eD1C4701C55", OrderVault: "0x31eF83a530Fde1B38EE9A18093A333D8Bbbc40D5", + ShiftVault: "0xfe99609C4AA83ff6816b64563Bdffd7fa68753Ab", SyntheticsReader: "0x5Ca84c34a381434786738735265b9f3FD814b824", SyntheticsRouter: "0x7452c558d45f8afC8c83dAe62C3f8A5BE19c71f6", @@ -243,6 +244,7 @@ const CONTRACTS = { DepositVault: "0x90c670825d0C62ede1c5ee9571d6d9a17A722DFF", WithdrawalVault: "0xf5F30B10141E1F63FC11eD772931A8294a591996", OrderVault: "0xD3D60D22d415aD43b7e64b510D86A30f19B1B12C", + ShiftVault: "0x7fC46CCb386e9bbBFB49A2639002734C3Ec52b39", SyntheticsReader: "0xBAD04dDcc5CC284A86493aFA75D2BEb970C72216", SyntheticsRouter: "0x820F5FfC5b525cD4d88Cd91aCf2c28F16530Cc68", @@ -298,6 +300,7 @@ const CONTRACTS = { DepositVault: "0x2964d242233036C8BDC1ADC795bB4DeA6fb929f2", WithdrawalVault: "0x74d49B6A630Bf519bDb6E4efc4354C420418A6A2", OrderVault: "0x25D23e8E655727F2687CC808BB9589525A6F599B", + ShiftVault: "0x257D0EA0B040E2Cd1D456fB4C66d7814102aD346", SyntheticsReader: "0xD52216D3A57F7eb1126498f00A4771553c737AE4", SyntheticsRouter: "0x5e7d61e4C52123ADF651961e4833aCc349b61491", Timelock: ZeroAddress, diff --git a/src/config/dataStore.ts b/src/config/dataStore.ts index 4ef76dbefc..958f179cb5 100644 --- a/src/config/dataStore.ts +++ b/src/config/dataStore.ts @@ -56,6 +56,7 @@ export const WITHDRAWAL_GAS_LIMIT_KEY = hashString("WITHDRAWAL_GAS_LIMIT"); export const INCREASE_ORDER_GAS_LIMIT_KEY = hashString("INCREASE_ORDER_GAS_LIMIT"); export const DECREASE_ORDER_GAS_LIMIT_KEY = hashString("DECREASE_ORDER_GAS_LIMIT"); export const SWAP_ORDER_GAS_LIMIT_KEY = hashString("SWAP_ORDER_GAS_LIMIT"); +export const SHIFT_GAS_LIMIT_KEY = hashString("SHIFT_GAS_LIMIT"); export const SINGLE_SWAP_GAS_LIMIT_KEY = hashString("SINGLE_SWAP_GAS_LIMIT"); export const TOKEN_TRANSFER_GAS_LIMIT_KEY = hashString("TOKEN_TRANSFER_GAS_LIMIT"); export const NATIVE_TOKEN_TRANSFER_GAS_LIMIT_KEY = hashString("NATIVE_TOKEN_TRANSFER_GAS_LIMIT"); @@ -221,6 +222,10 @@ export function withdrawalGasLimitKey() { return hashData(["bytes32"], [WITHDRAWAL_GAS_LIMIT_KEY]); } +export function shiftGasLimitKey() { + return SHIFT_GAS_LIMIT_KEY; +} + export function singleSwapGasLimitKey() { return SINGLE_SWAP_GAS_LIMIT_KEY; } diff --git a/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx b/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx index 51a1cea5db..02dfa19ec1 100644 --- a/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx +++ b/src/context/SyntheticsEvents/SyntheticsEventsProvider.tsx @@ -34,11 +34,14 @@ import { PendingDepositData, PendingFundingFeeSettlementData, PendingOrderData, - PendingPositionUpdate, PendingPositionsUpdates, + PendingPositionUpdate, + PendingShiftData, PendingWithdrawalData, PositionDecreaseEvent, PositionIncreaseEvent, + ShiftCreatedEventData, + ShiftStatuses, SyntheticsEventsContextType, WithdrawalCreatedEventData, WithdrawalStatuses, @@ -63,6 +66,7 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) const [orderStatuses, setOrderStatuses] = useState({}); const [depositStatuses, setDepositStatuses] = useState({}); const [withdrawalStatuses, setWithdrawalStatuses] = useState({}); + const [shiftStatuses, setShiftStatuses] = useState({}); const [pendingPositionsUpdates, setPendingPositionsUpdates] = useState({}); const [positionIncreaseEvents, setPositionIncreaseEvents] = useState([]); @@ -300,6 +304,50 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) } }, + ShiftCreated: (eventData: EventLogData, txnParams: EventTxnParams) => { + const data: ShiftCreatedEventData = { + key: eventData.bytes32Items.items.key, + account: eventData.addressItems.items.account, + receiver: eventData.addressItems.items.receiver, + callbackContract: eventData.addressItems.items.callbackContract, + fromMarket: eventData.addressItems.items.fromMarket, + toMarket: eventData.addressItems.items.toMarket, + marketTokenAmount: eventData.uintItems.items.marketTokenAmount, + minMarketTokens: eventData.uintItems.items.minMarketTokens, + updatedAtTime: eventData.uintItems.items.updatedAtTime, + executionFee: eventData.uintItems.items.executionFee, + }; + + if (data.account !== currentAccount) { + return; + } + + setShiftStatuses((old) => + setByKey(old, data.key, { + key: data.key, + data, + createdTxnHash: txnParams.transactionHash, + createdAt: Date.now(), + }) + ); + }, + + ShiftExecuted: (eventData: EventLogData, txnParams: EventTxnParams) => { + const key = eventData.bytes32Items.items.key; + + if (shiftStatuses[key]) { + setShiftStatuses((old) => updateByKey(old, key, { executedTxnHash: txnParams.transactionHash })); + } + }, + + ShiftCancelled: (eventData: EventLogData, txnParams: EventTxnParams) => { + const key = eventData.bytes32Items.items.key; + + if (shiftStatuses[key]) { + setShiftStatuses((old) => updateByKey(old, key, { cancelledTxnHash: txnParams.transactionHash })); + } + }, + PositionIncrease: (eventData: EventLogData, txnParams: EventTxnParams) => { const data: PositionIncreaseEvent = { positionKey: getPositionKey( @@ -452,6 +500,7 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) orderStatuses, depositStatuses, withdrawalStatuses, + shiftStatuses, pendingPositionsUpdates, positionIncreaseEvents, positionDecreaseEvents, @@ -520,6 +569,22 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) } ); }, + setPendingShift: (data: PendingShiftData) => { + const toastId = Date.now(); + + helperToast.success( + , + { + autoClose: false, + toastId, + } + ); + }, async setPendingPosition(update: PendingPositionUpdate) { setPendingPositionsUpdates((old) => setByKey(old, update.positionKey, update)); }, @@ -535,6 +600,10 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) setWithdrawalStatusViewed(key: string) { setWithdrawalStatuses((old) => updateByKey(old, key, { isViewed: true })); }, + + setShiftStatusViewed(key: string) { + setShiftStatuses((old) => updateByKey(old, key, { isViewed: true })); + }, }; }, [ depositStatuses, @@ -543,9 +612,10 @@ export function SyntheticsEventsProvider({ children }: { children: ReactNode }) pendingPositionsUpdates, positionDecreaseEvents, positionIncreaseEvents, + setPendingTxns, + shiftStatuses, tokensData, withdrawalStatuses, - setPendingTxns, ]); return {children}; diff --git a/src/context/SyntheticsEvents/types.ts b/src/context/SyntheticsEvents/types.ts index 85b96337bd..63da299632 100644 --- a/src/context/SyntheticsEvents/types.ts +++ b/src/context/SyntheticsEvents/types.ts @@ -4,6 +4,7 @@ export type SyntheticsEventsContextType = { orderStatuses: OrderStatuses; depositStatuses: DepositStatuses; withdrawalStatuses: WithdrawalStatuses; + shiftStatuses: ShiftStatuses; pendingPositionsUpdates: PendingPositionsUpdates; positionIncreaseEvents: PositionIncreaseEvent[] | undefined; positionDecreaseEvents: PositionDecreaseEvent[] | undefined; @@ -12,15 +13,18 @@ export type SyntheticsEventsContextType = { setPendingPosition: SetPendingPosition; setPendingDeposit: SetPendingDeposit; setPendingWithdrawal: SetPendingWithdrawal; + setPendingShift: SetPendingShift; setOrderStatusViewed: (key: string) => void; setDepositStatusViewed: (key: string) => void; setWithdrawalStatusViewed: (key: string) => void; + setShiftStatusViewed: (key: string) => void; }; export type SetPendingOrder = (data: PendingOrderData | PendingOrderData[]) => void; export type SetPendingPosition = (update: PendingPositionUpdate) => void; export type SetPendingDeposit = (data: PendingDepositData) => void; export type SetPendingWithdrawal = (data: PendingWithdrawalData) => void; +export type SetPendingShift = (data: PendingShiftData) => void; export type SetPendingFundingFeeSettlement = (data: PendingFundingFeeSettlementData) => void; export type PendingFundingFeeSettlementData = { @@ -121,6 +125,27 @@ export type PendingWithdrawalData = { shouldUnwrapNativeToken: boolean; }; +export type ShiftCreatedEventData = { + key: string; + account: string; + receiver: string; + callbackContract: string; + fromMarket: string; + toMarket: string; + marketTokenAmount: bigint; + minMarketTokens: bigint; + updatedAtTime: bigint; + executionFee: bigint; +}; + +export type PendingShiftData = { + account: string; + fromMarket: string; + marketTokenAmount: bigint; + toMarket: string; + minMarketTokens: bigint; +}; + export type MultiTransactionStatus = { key: string; data?: TEventData; @@ -135,6 +160,7 @@ export type MultiTransactionStatus = { export type OrderStatus = MultiTransactionStatus; export type DepositStatus = MultiTransactionStatus; export type WithdrawalStatus = MultiTransactionStatus; +export type ShiftStatus = MultiTransactionStatus; export type PositionIncreaseEvent = { positionKey: string; @@ -202,6 +228,10 @@ export type WithdrawalStatuses = { [key: string]: WithdrawalStatus; }; +export type ShiftStatuses = { + [key: string]: ShiftStatus; +}; + export type PendingPositionsUpdates = { [key: string]: PendingPositionUpdate | undefined; }; diff --git a/src/context/SyntheticsEvents/utils.ts b/src/context/SyntheticsEvents/utils.ts index 095ea92dba..4f0c995507 100644 --- a/src/context/SyntheticsEvents/utils.ts +++ b/src/context/SyntheticsEvents/utils.ts @@ -1,4 +1,4 @@ -import { PendingDepositData, PendingOrderData, PendingWithdrawalData } from "./types"; +import type { PendingDepositData, PendingOrderData, PendingShiftData, PendingWithdrawalData } from "./types"; export function getPendingOrderKey(data: Omit) { return [ @@ -48,3 +48,13 @@ export function getPendingWithdrawalKey(data: PendingWithdrawalData) { data.shouldUnwrapNativeToken, ].join(":"); } + +export function getPendingShiftKey(data: PendingShiftData) { + return [ + data.account, + data.fromMarket, + data.marketTokenAmount.toString(), + data.toMarket, + data.minMarketTokens.toString(), + ].join(":"); +} diff --git a/src/context/WebsocketContext/subscribeToEvents.ts b/src/context/WebsocketContext/subscribeToEvents.ts index e18c2a7034..f605c3a82b 100644 --- a/src/context/WebsocketContext/subscribeToEvents.ts +++ b/src/context/WebsocketContext/subscribeToEvents.ts @@ -50,6 +50,10 @@ const WITHDRAWAL_CREATED_HASH = ethers.id("WithdrawalCreated"); const WITHDRAWAL_EXECUTED_HASH = ethers.id("WithdrawalExecuted"); const WITHDRAWAL_CANCELLED_HASH = ethers.id("WithdrawalCancelled"); +const SHIFT_CREATED_HASH = ethers.id("ShiftCreated"); +const SHIFT_EXECUTED_HASH = ethers.id("ShiftExecuted"); +const SHIFT_CANCELLED_HASH = ethers.id("ShiftCancelled"); + const ORDER_CREATED_HASH = ethers.id("OrderCreated"); const ORDER_EXECUTED_HASH = ethers.id("OrderExecuted"); const ORDER_CANCELLED_HASH = ethers.id("OrderCancelled"); @@ -156,15 +160,15 @@ function createV2EventFilters(chainId: number, account: string, wsProvider: Prov const EVENT_LOG1_TOPIC = eventEmitter.interface.getEvent("EventLog1")?.topicHash ?? null; const EVENT_LOG2_TOPIC = eventEmitter.interface.getEvent("EventLog2")?.topicHash ?? null; return [ - // DEPOSITS AND WITHDRAWALS + // DEPOSITS AND WITHDRAWALS AND SHIFTS { address: getContract(chainId, "EventEmitter"), - topics: [EVENT_LOG2_TOPIC, [DEPOSIT_CREATED_HASH, WITHDRAWAL_CREATED_HASH], null, addressHash], - }, - // NEW CONTRACTS - { - address: getContract(chainId, "EventEmitter"), - topics: [EVENT_LOG2_TOPIC, [DEPOSIT_CREATED_HASH, WITHDRAWAL_CREATED_HASH], null, addressHash], + topics: [ + EVENT_LOG2_TOPIC, + [DEPOSIT_CREATED_HASH, WITHDRAWAL_CREATED_HASH, SHIFT_CREATED_HASH], + null, + addressHash, + ], }, { address: getContract(chainId, "EventEmitter"), @@ -178,7 +182,16 @@ function createV2EventFilters(chainId: number, account: string, wsProvider: Prov address: getContract(chainId, "EventEmitter"), topics: [ EVENT_LOG2_TOPIC, - [DEPOSIT_CANCELLED_HASH, DEPOSIT_EXECUTED_HASH, WITHDRAWAL_CANCELLED_HASH, WITHDRAWAL_EXECUTED_HASH], + [ + DEPOSIT_CANCELLED_HASH, + DEPOSIT_EXECUTED_HASH, + + WITHDRAWAL_CANCELLED_HASH, + WITHDRAWAL_EXECUTED_HASH, + + SHIFT_CANCELLED_HASH, + SHIFT_EXECUTED_HASH, + ], null, addressHash, ], diff --git a/src/domain/synthetics/fees/types.ts b/src/domain/synthetics/fees/types.ts index e2f71583b7..26705202e2 100644 --- a/src/domain/synthetics/fees/types.ts +++ b/src/domain/synthetics/fees/types.ts @@ -22,6 +22,7 @@ export type GasLimitsConfig = { depositSingleToken: bigint; depositMultiToken: bigint; withdrawalMultiToken: bigint; + shift: bigint; singleSwap: bigint; swapOrder: bigint; increaseOrder: bigint; diff --git a/src/domain/synthetics/fees/useGasLimits.ts b/src/domain/synthetics/fees/useGasLimits.ts index d9ddd71bfc..97565692aa 100644 --- a/src/domain/synthetics/fees/useGasLimits.ts +++ b/src/domain/synthetics/fees/useGasLimits.ts @@ -6,6 +6,7 @@ import { decreaseOrderGasLimitKey, depositGasLimitKey, increaseOrderGasLimitKey, + shiftGasLimitKey, singleSwapGasLimitKey, swapOrderGasLimitKey, withdrawalGasLimitKey, @@ -38,6 +39,10 @@ export function useGasLimits(chainId: number): GasLimitsConfig | undefined { methodName: "getUint", params: [withdrawalGasLimitKey()], }, + shift: { + methodName: "getUint", + params: [shiftGasLimitKey()], + }, singleSwap: { methodName: "getUint", params: [singleSwapGasLimitKey()], @@ -80,6 +85,7 @@ export function useGasLimits(chainId: number): GasLimitsConfig | undefined { depositSingleToken: getBigInt("depositSingleToken"), depositMultiToken: getBigInt("depositMultiToken"), withdrawalMultiToken: getBigInt("withdrawalMultiToken"), + shift: getBigInt("shift"), singleSwap: getBigInt("singleSwap"), swapOrder: getBigInt("swapOrder"), increaseOrder: getBigInt("increaseOrder"), diff --git a/src/domain/synthetics/fees/utils/estimateOraclePriceCount.ts b/src/domain/synthetics/fees/utils/estimateOraclePriceCount.ts index 813a53d51a..50c988f17f 100644 --- a/src/domain/synthetics/fees/utils/estimateOraclePriceCount.ts +++ b/src/domain/synthetics/fees/utils/estimateOraclePriceCount.ts @@ -10,3 +10,7 @@ export function estimateWithdrawalOraclePriceCount(swapsCount: number): bigint { export function estimateOrderOraclePriceCount(swapsCount: number): bigint { return 3n + BigInt(swapsCount); } + +export function estimateShiftOraclePriceCount(): bigint { + return 4n; +} diff --git a/src/domain/synthetics/fees/utils/executionFee.ts b/src/domain/synthetics/fees/utils/executionFee.ts index e3ed15dff3..4d5896ce49 100644 --- a/src/domain/synthetics/fees/utils/executionFee.ts +++ b/src/domain/synthetics/fees/utils/executionFee.ts @@ -97,6 +97,13 @@ export function estimateExecuteWithdrawalGasLimit( return gasLimits.withdrawalMultiToken + (withdrawal.callbackGasLimit ?? 0n); } +/** + * Copy from contract: `estimateExecuteShiftGasLimit` + */ +export function estimateExecuteShiftGasLimit(gasLimits: GasLimitsConfig, shift: { callbackGasLimit?: bigint }) { + return gasLimits.shift + (shift.callbackGasLimit ?? 0n); +} + /** * Copy from contract: `estimateExecuteIncreaseOrderGasLimit` */ diff --git a/src/domain/synthetics/markets/createDepositTxn.ts b/src/domain/synthetics/markets/createDepositTxn.ts index 2dd1c4f914..b421a048be 100644 --- a/src/domain/synthetics/markets/createDepositTxn.ts +++ b/src/domain/synthetics/markets/createDepositTxn.ts @@ -5,7 +5,7 @@ import ExchangeRouter from "abis/ExchangeRouter.json"; import { NATIVE_TOKEN_ADDRESS, convertTokenAddress } from "config/tokens"; import { SetPendingDeposit } from "context/SyntheticsEvents"; import { applySlippageToMinOut } from "../trade"; -import { simulateExecuteOrderTxn } from "../orders/simulateExecuteOrderTxn"; +import { simulateExecuteTxn } from "../orders/simulateExecuteTxn"; import { TokensData } from "../tokens"; import { UI_FEE_RECEIVER_ACCOUNT } from "config/ui"; import { t } from "@lingui/macro"; @@ -95,11 +95,11 @@ export async function createDepositTxn(chainId: number, signer: Signer, p: Param .map((call) => contract.interface.encodeFunctionData(call!.method, call!.params)); if (!p.skipSimulation) { - await simulateExecuteOrderTxn(chainId, { + await simulateExecuteTxn(chainId, { account: p.account, primaryPriceOverrides: {}, tokensData: p.tokensData, - createOrderMulticallPayload: encodedPayload, + createMulticallPayload: encodedPayload, method: "simulateExecuteDeposit", errorTitle: t`Deposit error.`, value: wntAmount, diff --git a/src/domain/synthetics/markets/createShiftTxn.ts b/src/domain/synthetics/markets/createShiftTxn.ts new file mode 100644 index 0000000000..3b9dee9e8e --- /dev/null +++ b/src/domain/synthetics/markets/createShiftTxn.ts @@ -0,0 +1,85 @@ +import { t } from "@lingui/macro"; +import { Signer, ethers } from "ethers"; + +import { getContract } from "config/contracts"; +import { UI_FEE_RECEIVER_ACCOUNT } from "config/ui"; +import { SetPendingShift } from "context/SyntheticsEvents"; +import { callContract } from "lib/contracts"; + +import { simulateExecuteTxn } from "../orders/simulateExecuteTxn"; +import type { TokensData } from "../tokens"; +import { applySlippageToMinOut } from "../trade"; + +import ExchangeRouter from "abis/ExchangeRouter.json"; + +type Params = { + account: string; + fromMarketTokenAddress: string; + fromMarketTokenAmount: bigint; + toMarketTokenAddress: string; + minToMarketTokenAmount: bigint; + executionFee: bigint; + allowedSlippage: number; + tokensData: TokensData; + skipSimulation?: boolean; + setPendingTxns: (txns: any) => void; + setPendingShift: SetPendingShift; +}; + +export async function createShiftTxn(chainId: number, signer: Signer, p: Params) { + const contract = new ethers.Contract(getContract(chainId, "ExchangeRouter"), ExchangeRouter.abi, signer); + const shiftVaultAddress = getContract(chainId, "ShiftVault"); + + const minToMarketTokenAmount = applySlippageToMinOut(p.allowedSlippage, p.minToMarketTokenAmount); + + const multicall = [ + { method: "sendWnt", params: [shiftVaultAddress, p.executionFee] }, + { method: "sendTokens", params: [p.fromMarketTokenAddress, shiftVaultAddress, p.fromMarketTokenAmount] }, + { + method: "createShift", + params: [ + { + receiver: p.account, + callbackContract: ethers.ZeroAddress, + uiFeeReceiver: UI_FEE_RECEIVER_ACCOUNT ?? ethers.ZeroAddress, + fromMarket: p.fromMarketTokenAddress, + toMarket: p.toMarketTokenAddress, + minMarketTokens: minToMarketTokenAmount, + executionFee: p.executionFee, + callbackGasLimit: 0n, + }, + ], + }, + ]; + + const encodedPayload = multicall + .filter(Boolean) + .map((call) => contract.interface.encodeFunctionData(call!.method, call!.params)); + + if (!p.skipSimulation) { + await simulateExecuteTxn(chainId, { + account: p.account, + primaryPriceOverrides: {}, + tokensData: p.tokensData, + createMulticallPayload: encodedPayload, + method: "simulateExecuteShift", + errorTitle: t`Shift error.`, + value: p.executionFee, + }); + } + + return callContract(chainId, contract, "multicall", [encodedPayload], { + value: p.executionFee, + hideSentMsg: true, + hideSuccessMsg: true, + setPendingTxns: p.setPendingTxns, + }).then(() => { + p.setPendingShift({ + account: p.account, + fromMarket: p.fromMarketTokenAddress, + marketTokenAmount: p.fromMarketTokenAmount, + toMarket: p.toMarketTokenAddress, + minMarketTokens: minToMarketTokenAmount, + }); + }); +} diff --git a/src/domain/synthetics/markets/createWithdrawalTxn.ts b/src/domain/synthetics/markets/createWithdrawalTxn.ts index 54902b4b64..1a1dc2978d 100644 --- a/src/domain/synthetics/markets/createWithdrawalTxn.ts +++ b/src/domain/synthetics/markets/createWithdrawalTxn.ts @@ -7,7 +7,7 @@ import { callContract } from "lib/contracts"; import { isAddressZero } from "lib/legacy"; import { applySlippageToMinOut } from "../trade"; import { TokensData } from "../tokens"; -import { simulateExecuteOrderTxn } from "../orders/simulateExecuteOrderTxn"; +import { simulateExecuteTxn } from "../orders/simulateExecuteTxn"; import { UI_FEE_RECEIVER_ACCOUNT } from "config/ui"; import { t } from "@lingui/macro"; import { SwapPricingType } from "../orders"; @@ -75,11 +75,11 @@ export async function createWithdrawalTxn(chainId: number, signer: Signer, p: Pa .map((call) => contract.interface.encodeFunctionData(call!.method, call!.params)); if (!p.skipSimulation) { - await simulateExecuteOrderTxn(chainId, { + await simulateExecuteTxn(chainId, { account: p.account, primaryPriceOverrides: {}, tokensData: p.tokensData, - createOrderMulticallPayload: encodedPayload, + createMulticallPayload: encodedPayload, method: "simulateExecuteWithdrawal", errorTitle: t`Withdrawal error.`, value: wntAmount, diff --git a/src/domain/synthetics/orders/createDecreaseOrderTxn.ts b/src/domain/synthetics/orders/createDecreaseOrderTxn.ts index 40644e67f6..bfe1da8bf2 100644 --- a/src/domain/synthetics/orders/createDecreaseOrderTxn.ts +++ b/src/domain/synthetics/orders/createDecreaseOrderTxn.ts @@ -8,7 +8,7 @@ import { Signer, ethers } from "ethers"; import { callContract } from "lib/contracts"; import { getPositionKey } from "../positions"; import { applySlippageToMinOut, applySlippageToPrice } from "../trade"; -import { PriceOverrides, simulateExecuteOrderTxn } from "./simulateExecuteOrderTxn"; +import { PriceOverrides, simulateExecuteTxn } from "./simulateExecuteTxn"; import { DecreasePositionSwapType, OrderType } from "./types"; import { isMarketOrderType, getPendingOrderFromParams } from "./utils"; import { t } from "@lingui/macro"; @@ -93,10 +93,10 @@ export async function createDecreaseOrderTxn( maxPrice: p.triggerPrice, }; } - await simulateExecuteOrderTxn(chainId, { + await simulateExecuteTxn(chainId, { account, primaryPriceOverrides, - createOrderMulticallPayload: simulationEncodedPayload, + createMulticallPayload: simulationEncodedPayload, value: totalWntAmount, tokensData: p.tokensData, errorTitle: t`Order error.`, diff --git a/src/domain/synthetics/orders/createIncreaseOrderTxn.ts b/src/domain/synthetics/orders/createIncreaseOrderTxn.ts index 304499f76a..c2fcb52564 100644 --- a/src/domain/synthetics/orders/createIncreaseOrderTxn.ts +++ b/src/domain/synthetics/orders/createIncreaseOrderTxn.ts @@ -5,7 +5,7 @@ import { SetPendingOrder, SetPendingPosition, PendingOrderData } from "context/S import { TokenData, TokensData, convertToContractPrice } from "domain/synthetics/tokens"; import { Signer, ethers } from "ethers"; import { callContract } from "lib/contracts"; -import { PriceOverrides, simulateExecuteOrderTxn } from "./simulateExecuteOrderTxn"; +import { PriceOverrides, simulateExecuteTxn } from "./simulateExecuteTxn"; import { DecreasePositionSwapType, OrderType, OrderTxnType } from "./types"; import { isMarketOrderType, getPendingOrderFromParams } from "./utils"; import { getPositionKey } from "../positions"; @@ -207,11 +207,11 @@ export async function createIncreaseOrderTxn({ } if (!p.skipSimulation) { - await simulateExecuteOrderTxn(chainId, { + await simulateExecuteTxn(chainId, { account: p.account, tokensData: p.tokensData, primaryPriceOverrides, - createOrderMulticallPayload: simulationEncodedPayload, + createMulticallPayload: simulationEncodedPayload, value: totalWntAmount, errorTitle: t`Order error.`, }); diff --git a/src/domain/synthetics/orders/createSwapOrderTxn.ts b/src/domain/synthetics/orders/createSwapOrderTxn.ts index 118dcf2c8a..7e9865c438 100644 --- a/src/domain/synthetics/orders/createSwapOrderTxn.ts +++ b/src/domain/synthetics/orders/createSwapOrderTxn.ts @@ -5,7 +5,7 @@ import { SetPendingOrder, PendingOrderData } from "context/SyntheticsEvents"; import { Signer, ethers } from "ethers"; import { callContract } from "lib/contracts"; import { TokensData } from "../tokens"; -import { simulateExecuteOrderTxn } from "./simulateExecuteOrderTxn"; +import { simulateExecuteTxn } from "./simulateExecuteTxn"; import { DecreasePositionSwapType, OrderType } from "./types"; import { applySlippageToMinOut } from "../trade"; import { isMarketOrderType } from "./utils"; @@ -69,10 +69,10 @@ export async function createSwapOrderTxn(chainId: number, signer: Signer, subacc } if (p.orderType !== OrderType.LimitSwap) { - await simulateExecuteOrderTxn(chainId, { + await simulateExecuteTxn(chainId, { account: p.account, primaryPriceOverrides: {}, - createOrderMulticallPayload: simulationEncodedPayload, + createMulticallPayload: simulationEncodedPayload, value: sumaltionTotalWntAmount, tokensData: p.tokensData, errorTitle: t`Order error.`, diff --git a/src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx b/src/domain/synthetics/orders/simulateExecuteTxn.tsx similarity index 93% rename from src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx rename to src/domain/synthetics/orders/simulateExecuteTxn.tsx index 6944bdf6ff..c2ebc7c529 100644 --- a/src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +++ b/src/domain/synthetics/orders/simulateExecuteTxn.tsx @@ -17,18 +17,18 @@ export type PriceOverrides = { [address: string]: TokenPrices | undefined; }; -type SimulateExecuteOrderParams = { +type SimulateExecuteParams = { account: string; - createOrderMulticallPayload: string[]; + createMulticallPayload: string[]; primaryPriceOverrides: PriceOverrides; tokensData: TokensData; value: bigint; - method?: "simulateExecuteDeposit" | "simulateExecuteWithdrawal" | "simulateExecuteOrder"; + method?: "simulateExecuteDeposit" | "simulateExecuteWithdrawal" | "simulateExecuteOrder" | "simulateExecuteShift"; errorTitle?: string; swapPricingType?: SwapPricingType; }; -export async function simulateExecuteOrderTxn(chainId: number, p: SimulateExecuteOrderParams) { +export async function simulateExecuteTxn(chainId: number, p: SimulateExecuteParams) { const provider = getProvider(undefined, chainId); const dataStoreAddress = getContract(chainId, "DataStore"); @@ -65,7 +65,7 @@ export async function simulateExecuteOrderTxn(chainId: number, p: SimulateExecut maxTimestamp: priceTimestamp, } as OracleUtils.SimulatePricesParamsStruct; - let simulationPayloadData = [...p.createOrderMulticallPayload]; + let simulationPayloadData = [...p.createMulticallPayload]; if (method === "simulateExecuteWithdrawal") { if (p.swapPricingType === undefined) { @@ -87,6 +87,10 @@ export async function simulateExecuteOrderTxn(chainId: number, p: SimulateExecut simulationPayloadData.push( exchangeRouter.interface.encodeFunctionData("simulateExecuteOrder", [nextKey, simulationPriceParams]) ); + } else if (method === "simulateExecuteShift") { + simulationPayloadData.push( + exchangeRouter.interface.encodeFunctionData("simulateExecuteShift", [nextKey, simulationPriceParams]) + ); } else { throw new Error(`Unknown method: ${method}`); } diff --git a/src/domain/synthetics/trade/types.ts b/src/domain/synthetics/trade/types.ts index 2a92083467..78e43a7b89 100644 --- a/src/domain/synthetics/trade/types.ts +++ b/src/domain/synthetics/trade/types.ts @@ -241,6 +241,7 @@ export type GmSwapFees = { swapFee?: FeeItem; swapPriceImpact?: FeeItem; uiFee?: FeeItem; + shiftFee?: FeeItem; }; export type TradeSearchParams = { diff --git a/src/domain/synthetics/trade/utils/deposit.ts b/src/domain/synthetics/trade/utils/deposit.ts index c3dd1272c4..e29f8c7cac 100644 --- a/src/domain/synthetics/trade/utils/deposit.ts +++ b/src/domain/synthetics/trade/utils/deposit.ts @@ -17,6 +17,7 @@ export function getDepositAmounts(p: { includeLongToken: boolean; includeShortToken: boolean; uiFeeFactor: bigint; + forShift?: boolean; }): DepositAmounts { const { marketInfo, @@ -68,8 +69,10 @@ export function getDepositAmounts(p: { const totalDepositUsd = values.longTokenUsd + values.shortTokenUsd; if (values.longTokenUsd > 0) { - const swapFeeUsd = getSwapFee(marketInfo, values.longTokenUsd, values.swapPriceImpactDeltaUsd > 0); - values.swapFeeUsd = values.swapFeeUsd + swapFeeUsd; + if (!p.forShift) { + const swapFeeUsd = getSwapFee(marketInfo, values.longTokenUsd, values.swapPriceImpactDeltaUsd > 0); + values.swapFeeUsd = values.swapFeeUsd + swapFeeUsd; + } const uiFeeUsd = applyFactor(values.longTokenUsd, uiFeeFactor); values.uiFeeUsd = values.uiFeeUsd + uiFeeUsd; @@ -83,14 +86,16 @@ export function getDepositAmounts(p: { tokenOut: shortToken, amount: values.longTokenAmount, priceImpactDeltaUsd: bigMath.mulDiv(values.swapPriceImpactDeltaUsd, values.longTokenUsd, totalDepositUsd), - swapFeeUsd, + swapFeeUsd: values.swapFeeUsd, uiFeeUsd, }); } if (values.shortTokenUsd > 0) { - const swapFeeUsd = getSwapFee(marketInfo, values.shortTokenUsd, values.swapPriceImpactDeltaUsd > 0); - values.swapFeeUsd = values.swapFeeUsd + swapFeeUsd; + if (!p.forShift) { + const swapFeeUsd = getSwapFee(marketInfo, values.shortTokenUsd, values.swapPriceImpactDeltaUsd > 0); + values.swapFeeUsd = values.swapFeeUsd + swapFeeUsd; + } const uiFeeUsd = applyFactor(values.shortTokenUsd, uiFeeFactor); values.uiFeeUsd = values.uiFeeUsd + uiFeeUsd; @@ -104,7 +109,7 @@ export function getDepositAmounts(p: { tokenOut: longToken, amount: values.shortTokenAmount, priceImpactDeltaUsd: bigMath.mulDiv(values.swapPriceImpactDeltaUsd, values.shortTokenUsd, totalDepositUsd), - swapFeeUsd, + swapFeeUsd: values.swapFeeUsd, uiFeeUsd, }); } @@ -122,7 +127,18 @@ export function getDepositAmounts(p: { const prevShortTokenUsd = convertToUsd(shortTokenAmount, shortToken.decimals, shortTokenPrice)!; const prevSumUsd = prevLongTokenUsd + prevShortTokenUsd; - if (includeLongToken && includeShortToken && prevSumUsd > 0) { + const longPoolAmount = marketInfo.longPoolAmount; + const shortPoolAmount = marketInfo.shortPoolAmount; + + const longPoolUsd = convertToUsd(longPoolAmount, longToken.decimals, longToken.prices.maxPrice)!; + const shortPoolUsd = convertToUsd(shortPoolAmount, shortToken.decimals, shortToken.prices.maxPrice)!; + const totalPoolUsd = longPoolUsd + shortPoolUsd; + + if (p.forShift) { + // Reverse the withdrawal amounts + values.longTokenUsd = bigMath.mulDiv(values.marketTokenUsd, shortPoolUsd, totalPoolUsd); + values.shortTokenUsd = bigMath.mulDiv(values.marketTokenUsd, longPoolUsd, totalPoolUsd); + } else if (includeLongToken && includeShortToken && prevSumUsd > 0) { values.longTokenUsd = bigMath.mulDiv(values.marketTokenUsd, prevLongTokenUsd, prevSumUsd); values.shortTokenUsd = values.marketTokenUsd - values.longTokenUsd; } else if (includeLongToken) { @@ -139,8 +155,10 @@ export function getDepositAmounts(p: { values.shortTokenUsd ); - const swapFeeUsd = getSwapFee(marketInfo, values.marketTokenUsd, values.swapPriceImpactDeltaUsd > 0); - values.swapFeeUsd = values.swapFeeUsd + swapFeeUsd; + if (!p.forShift) { + const swapFeeUsd = getSwapFee(marketInfo, values.marketTokenUsd, values.swapPriceImpactDeltaUsd > 0); + values.swapFeeUsd = values.swapFeeUsd + swapFeeUsd; + } const uiFeeUsd = applyFactor(values.marketTokenUsd, uiFeeFactor); values.uiFeeUsd = values.uiFeeUsd + uiFeeUsd; diff --git a/src/domain/synthetics/trade/utils/shift.ts b/src/domain/synthetics/trade/utils/shift.ts new file mode 100644 index 0000000000..7cfa1974f9 --- /dev/null +++ b/src/domain/synthetics/trade/utils/shift.ts @@ -0,0 +1,120 @@ +import type { MarketInfo } from "domain/synthetics/markets/types"; +import { convertToUsd, getMidPrice } from "domain/synthetics/tokens"; +import type { TokenData } from "domain/synthetics/tokens/types"; + +import { getDepositAmounts } from "./deposit"; +import { getWithdrawalAmounts } from "./withdrawal"; + +type ShiftAmounts = { + fromTokenAmount: bigint; + fromTokenUsd: bigint; + toTokenAmount: bigint; + toTokenUsd: bigint; + uiFeeUsd: bigint; + swapPriceImpactDeltaUsd: bigint; +}; + +export function getShiftAmounts({ + strategy, + fromToken, + fromMarketInfo, + toToken, + toMarketInfo, + fromTokenAmount, + toTokenAmount, + uiFeeFactor, +}: { + strategy: "byFromToken" | "byToToken"; + fromToken: TokenData; + fromMarketInfo: MarketInfo; + toToken: TokenData; + toMarketInfo: MarketInfo; + fromTokenAmount: bigint; + toTokenAmount: bigint; + uiFeeFactor: bigint; +}): ShiftAmounts { + const values: ShiftAmounts = { + fromTokenAmount: 0n, + fromTokenUsd: 0n, + toTokenAmount: 0n, + toTokenUsd: 0n, + uiFeeUsd: 0n, + swapPriceImpactDeltaUsd: 0n, + }; + const fromTokenPrice = getMidPrice(fromToken.prices); + const toTokenPrice = getMidPrice(toToken.prices); + + if (strategy === "byFromToken") { + values.fromTokenAmount = fromTokenAmount; + values.fromTokenUsd = convertToUsd(fromTokenAmount, fromToken.decimals, fromTokenPrice)!; + + const withdrawalAmounts = getWithdrawalAmounts({ + marketInfo: fromMarketInfo, + marketToken: fromToken, + marketTokenAmount: fromTokenAmount, + uiFeeFactor: uiFeeFactor, + strategy: "byMarketToken", + longTokenAmount: 0n, + shortTokenAmount: 0n, + forShift: true, + }); + + const depositAmounts = getDepositAmounts({ + marketInfo: toMarketInfo, + marketToken: toToken, + longToken: toMarketInfo.longToken, + shortToken: toMarketInfo.shortToken, + longTokenAmount: withdrawalAmounts.longTokenAmount, + shortTokenAmount: withdrawalAmounts.shortTokenAmount, + marketTokenAmount: 0n, + strategy: "byCollaterals", + includeLongToken: false, + includeShortToken: false, + uiFeeFactor: 0n, + forShift: true, + }); + + values.uiFeeUsd = withdrawalAmounts.uiFeeUsd; + values.swapPriceImpactDeltaUsd = depositAmounts.swapPriceImpactDeltaUsd; + + values.toTokenAmount = depositAmounts.marketTokenAmount; + values.toTokenUsd = convertToUsd(depositAmounts.marketTokenAmount, toToken.decimals, toTokenPrice)!; + } else { + values.toTokenAmount = toTokenAmount; + values.toTokenUsd = convertToUsd(toTokenAmount, toToken.decimals, toTokenPrice)!; + + const depositAmounts = getDepositAmounts({ + marketInfo: toMarketInfo, + marketToken: toToken, + marketTokenAmount: toTokenAmount, + strategy: "byMarketToken", + longToken: toMarketInfo.longToken, + shortToken: toMarketInfo.shortToken, + longTokenAmount: 0n, + shortTokenAmount: 0n, + includeLongToken: true, + includeShortToken: true, + uiFeeFactor: 0n, + forShift: true, + }); + + const withdrawalAmounts = getWithdrawalAmounts({ + marketInfo: fromMarketInfo, + marketToken: fromToken, + longTokenAmount: depositAmounts.longTokenAmount, + shortTokenAmount: depositAmounts.shortTokenAmount, + strategy: "byCollaterals", + marketTokenAmount: 0n, + uiFeeFactor: uiFeeFactor, + forShift: true, + }); + + values.uiFeeUsd = withdrawalAmounts.uiFeeUsd; + values.swapPriceImpactDeltaUsd = depositAmounts.swapPriceImpactDeltaUsd; + + values.fromTokenAmount = withdrawalAmounts.marketTokenAmount; + values.fromTokenUsd = convertToUsd(withdrawalAmounts.marketTokenAmount, fromToken.decimals, fromTokenPrice)!; + } + + return values; +} diff --git a/src/domain/synthetics/trade/utils/validation.ts b/src/domain/synthetics/trade/utils/validation.ts index d05e1bdf5e..a59f25fdd8 100644 --- a/src/domain/synthetics/trade/utils/validation.ts +++ b/src/domain/synthetics/trade/utils/validation.ts @@ -650,6 +650,79 @@ export function getGmSwapError(p: { return [undefined]; } +export function getGmShiftError({ + fromMarketInfo, + fromToken, + fromTokenAmount, + fromTokenUsd, + toMarketInfo, + toToken, + toTokenAmount, + fees, + isHighPriceImpact, + isHighPriceImpactAccepted, + priceImpactUsd, +}: { + fromMarketInfo: MarketInfo | undefined; + fromToken: TokenData | undefined; + fromTokenAmount: bigint | undefined; + fromTokenUsd: bigint | undefined; + toMarketInfo: MarketInfo | undefined; + toToken: TokenData | undefined; + toTokenAmount: bigint | undefined; + fees: GmSwapFees | undefined; + isHighPriceImpact: boolean; + isHighPriceImpactAccepted: boolean; + priceImpactUsd: bigint | undefined; +}) { + if (!fromMarketInfo || !fromToken || !toMarketInfo || !toToken) { + return [t`Loading...`]; + } + + if (isHighPriceImpact && !isHighPriceImpactAccepted) { + return [t`Price Impact not yet acknowledged`]; + } + + if (priceImpactUsd !== undefined && priceImpactUsd > 0) { + const { impactAmount } = applySwapImpactWithCap(toMarketInfo, priceImpactUsd); + const newPoolAmount = applyDeltaToPoolAmount(toMarketInfo, impactAmount); + + if (!getIsValidPoolAmount(toMarketInfo, newPoolAmount)) { + return [t`Max pool amount exceeded`]; + } + } + + if (!getIsValidPoolUsdForDeposit(toMarketInfo)) { + return [t`Max pool USD exceeded`]; + } + + const totalCollateralUsd = fromTokenUsd ?? 0n; + + const mintableInfo = getMintableMarketTokens(toMarketInfo, toToken); + + if (toTokenAmount !== undefined && toTokenAmount > mintableInfo.mintableAmount) { + return [t`Max ${toToken?.symbol} amount exceeded`]; + } + + const feesExistAndNegative = fees?.totalFees?.deltaUsd === undefined ? undefined : fees?.totalFees?.deltaUsd < 0; + if (feesExistAndNegative && bigMath.abs(fees?.totalFees?.deltaUsd ?? 0n) > totalCollateralUsd) { + return [t`Fees exceed Pay amount`]; + } + + if ((fromTokenAmount ?? 0n) < 0 || (toTokenAmount ?? 0n) < 0) { + return [t`Amount should be greater than zero`]; + } + + if (fromTokenAmount === undefined || fromTokenAmount < 0 || toTokenAmount === undefined || toTokenAmount < 0) { + return [t`Enter an amount`]; + } + + if ((fromTokenAmount ?? 0n) > (fromToken?.balance ?? 0n)) { + return [t`Insufficient ${fromToken?.symbol} balance`]; + } + + return [undefined]; +} function getIsValidPoolUsdForDeposit(marketInfo: MarketInfo) { const tokenIn = getTokenIn(marketInfo); diff --git a/src/domain/synthetics/trade/utils/withdrawal.ts b/src/domain/synthetics/trade/utils/withdrawal.ts index 65ed54e6b3..21a72616dd 100644 --- a/src/domain/synthetics/trade/utils/withdrawal.ts +++ b/src/domain/synthetics/trade/utils/withdrawal.ts @@ -1,8 +1,9 @@ import { MarketInfo, marketTokenAmountToUsd, usdToMarketTokenAmount } from "domain/synthetics/markets"; import { TokenData, convertToTokenAmount, convertToUsd } from "domain/synthetics/tokens"; +import { bigMath } from "lib/bigmath"; import { applyFactor } from "lib/numbers"; + import { WithdrawalAmounts } from "../types"; -import { bigMath } from "lib/bigmath"; export function getWithdrawalAmounts(p: { marketInfo: MarketInfo; @@ -11,7 +12,8 @@ export function getWithdrawalAmounts(p: { longTokenAmount: bigint; shortTokenAmount: bigint; uiFeeFactor: bigint; - strategy: "byMarketToken" | "byLongCollateral" | "byShortCollateral"; + strategy: "byMarketToken" | "byLongCollateral" | "byShortCollateral" | "byCollaterals"; + forShift?: boolean; }) { const { marketInfo, marketToken, marketTokenAmount, longTokenAmount, shortTokenAmount, uiFeeFactor, strategy } = p; @@ -48,8 +50,13 @@ export function getWithdrawalAmounts(p: { values.longTokenUsd = bigMath.mulDiv(values.marketTokenUsd, longPoolUsd, totalPoolUsd); values.shortTokenUsd = bigMath.mulDiv(values.marketTokenUsd, shortPoolUsd, totalPoolUsd); - const longSwapFeeUsd = applyFactor(values.longTokenUsd, p.marketInfo.swapFeeFactorForNegativeImpact); - const shortSwapFeeUsd = applyFactor(values.shortTokenUsd, p.marketInfo.swapFeeFactorForNegativeImpact); + const longSwapFeeUsd = p.forShift + ? 0n + : applyFactor(values.longTokenUsd, p.marketInfo.swapFeeFactorForNegativeImpact); + const shortSwapFeeUsd = p.forShift + ? 0n + : applyFactor(values.shortTokenUsd, p.marketInfo.swapFeeFactorForNegativeImpact); + const longUiFeeUsd = applyFactor(values.marketTokenUsd, uiFeeFactor); const shortUiFeeUsd = applyFactor(values.shortTokenUsd, uiFeeFactor); @@ -84,10 +91,23 @@ export function getWithdrawalAmounts(p: { longToken.decimals, longToken.prices.maxPrice )!; + } else if (strategy === "byCollaterals") { + values.longTokenAmount = longTokenAmount; + values.longTokenUsd = convertToUsd(longTokenAmount, longToken.decimals, longToken.prices.maxPrice)!; + values.shortTokenAmount = shortTokenAmount; + values.shortTokenUsd = convertToUsd(shortTokenAmount, shortToken.decimals, shortToken.prices.maxPrice)!; + + values.uiFeeUsd = applyFactor(values.longTokenUsd + values.shortTokenUsd, uiFeeFactor); + values.marketTokenUsd += values.uiFeeUsd; } - values.marketTokenUsd = values.longTokenUsd + values.shortTokenUsd; - values.swapFeeUsd = applyFactor(values.marketTokenUsd, p.marketInfo.swapFeeFactorForNegativeImpact); + values.marketTokenUsd = values.marketTokenUsd + values.longTokenUsd + values.shortTokenUsd; + if (!p.forShift) { + values.swapFeeUsd = applyFactor( + values.longTokenUsd + values.shortTokenUsd, + p.marketInfo.swapFeeFactorForNegativeImpact + ); + } values.marketTokenUsd = values.marketTokenUsd + values.swapFeeUsd; values.marketTokenAmount = usdToMarketTokenAmount(marketInfo, marketToken, values.marketTokenUsd)!; diff --git a/src/lib/errorReporting.ts b/src/lib/errorReporting.ts index e2087d6fbd..fb88989a9b 100644 --- a/src/lib/errorReporting.ts +++ b/src/lib/errorReporting.ts @@ -2,7 +2,7 @@ import { getAccount } from "@wagmi/core"; import CustomErrors from "abis/CustomErrors.json"; import { isDevelopment, isLocal } from "config/env"; import cryptoJs from "crypto-js"; -import { extractDataFromError } from "domain/synthetics/orders/simulateExecuteOrderTxn"; +import { extractDataFromError } from "domain/synthetics/orders/simulateExecuteTxn"; import { OracleFetcher, useOracleKeeperFetcher } from "domain/synthetics/tokens"; import { ethers } from "ethers"; import { useEffect } from "react"; diff --git a/src/pages/MarketPoolsPage/MarketPoolsPage.tsx b/src/pages/MarketPoolsPage/MarketPoolsPage.tsx index a7367cac84..b1a164fdc2 100644 --- a/src/pages/MarketPoolsPage/MarketPoolsPage.tsx +++ b/src/pages/MarketPoolsPage/MarketPoolsPage.tsx @@ -1,8 +1,10 @@ import { Trans } from "@lingui/macro"; import { useEffect, useRef, useState } from "react"; +import { Mode, Operation } from "components/Synthetics/GmSwap/GmSwapBox/types"; import { getSyntheticsDepositMarketKey } from "config/localStorage"; import { MarketsInfoData, useMarketsInfoRequest, useMarketTokensData } from "domain/synthetics/markets"; +import { useGmMarketsApy } from "domain/synthetics/markets/useGmMarketsApy"; import { getTokenData } from "domain/synthetics/tokens"; import { useChainId } from "lib/chains"; import { getPageTitle } from "lib/legacy"; @@ -15,11 +17,10 @@ import Footer from "components/Footer/Footer"; import PageTitle from "components/PageTitle/PageTitle"; import { GmList } from "components/Synthetics/GmList/GmList"; import { getGmSwapBoxAvailableModes } from "components/Synthetics/GmSwap/GmSwapBox/getGmSwapBoxAvailableModes"; -import { GmSwapBox, Mode, Operation } from "components/Synthetics/GmSwap/GmSwapBox/GmSwapBox"; +import { GmSwapBox } from "components/Synthetics/GmSwap/GmSwapBox/GmSwapBox"; import { MarketStats } from "components/Synthetics/MarketStats/MarketStats"; import "./MarketPoolsPage.scss"; -import { useGmMarketsApy } from "domain/synthetics/markets/useGmMarketsApy"; export function MarketPoolsPage() { const { chainId } = useChainId(); From 615433db68e61784bde6d44ef77deeaac92f2557 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Mon, 5 Aug 2024 16:41:56 +0000 Subject: [PATCH 19/62] Shifts add network fee --- .../Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx index 839e354d5a..4011732892 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx @@ -40,6 +40,7 @@ import Button from "components/Button/Button"; import BuyInputSection from "components/BuyInputSection/BuyInputSection"; import { ExchangeInfo } from "components/Exchange/ExchangeInfo"; import { PoolSelector } from "components/MarketSelector/PoolSelector"; +import { NetworkFeeRow } from "components/Synthetics/NetworkFeeRow/NetworkFeeRow"; import { GmConfirmationBox } from "../../GmConfirmationBox/GmConfirmationBox"; import { GmFees } from "../../GmFees/GmFees"; import { Swap } from "../Swap"; @@ -349,6 +350,7 @@ export function GmShiftBox({ uiFee={fees?.uiFee} shiftFee={fees?.shiftFee} /> +
From 9133ba112c568fbb731aeb7e89d3433c28fc99dd Mon Sep 17 00:00:00 2001 From: midas-myth Date: Mon, 5 Aug 2024 16:47:38 +0000 Subject: [PATCH 20/62] Move more function is gm shift box to useCallback --- .../GmSwapBox/GmShiftBox/GmShiftBox.tsx | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx index 4011732892..ca4dd40a9b 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx @@ -270,19 +270,29 @@ export function GmShiftBox({ if (!selectedToken || selectedToken.balance === undefined) return; setSelectedMarketText(formatAmountFree(selectedToken.balance, selectedToken.decimals)); }, [selectedToken]); - const handleSelectedTokenInputValueChange = useCallback( (event: React.ChangeEvent) => setSelectedMarketText(event.target.value), [] ); - const handleSelectedTokenFocus = useCallback(() => setFocusedInput("selectedMarket"), []); - const handleSelectedTokenSelectMarket = useCallback( (marketInfo: MarketInfo): void => onSelectMarket(marketInfo.marketTokenAddress), [onSelectMarket] ); + const handleToTokenInputValueChange = useCallback( + (event: React.ChangeEvent) => setToMarketText(event.target.value), + [] + ); + const handleToTokenFocus = useCallback(() => setFocusedInput("toMarket"), []); + const handleToTokenSelectMarket = useCallback( + (marketInfo: MarketInfo): void => setToMarketAddress(marketInfo.marketTokenAddress), + [] + ); + const handleSubmittedOrClosed = useCallback(() => { + setIsConfirmationBoxVisible(false); + }, []); + return ( <>
@@ -325,13 +335,13 @@ export function GmShiftBox({ useCommas: true, })} inputValue={toMarketText} - onInputValueChange={(event) => setToMarketText(event.target.value)} - onFocus={() => setFocusedInput("toMarket")} + onInputValueChange={handleToTokenInputValueChange} + onFocus={handleToTokenFocus} > setToMarketAddress(marketInfo.marketTokenAddress)} + onSelectMarket={handleToTokenSelectMarket} selectedIndexName={toIndexName} showAllPools isSideMenu @@ -372,12 +382,8 @@ export function GmShiftBox({ error={submitState.error} operation={Operation.Shift} executionFee={executionFee} - onSubmitted={() => { - setIsConfirmationBoxVisible(false); - }} - onClose={() => { - setIsConfirmationBoxVisible(false); - }} + onSubmitted={handleSubmittedOrClosed} + onClose={handleSubmittedOrClosed} shouldDisableValidation={shouldDisableValidationForTesting} /> From 9f05ed6e1360b0ea55d885f4952932188617255f Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 6 Aug 2024 10:59:19 +0000 Subject: [PATCH 21/62] Shift button in gmlist --- src/components/Button/Button.scss | 8 +- src/components/Button/ButtonLink.tsx | 6 +- .../Skeleton/GMListSkeletonStructure.tsx | 8 +- src/components/Synthetics/GmList/GmList.tsx | 264 ++++++++++-------- .../GmDepositWithdrawalBox.tsx | 40 +-- .../useGmDepositWithdrawalBoxState.tsx | 7 - .../useUpdateInputAmounts.tsx | 0 .../useUpdateTokens.tsx | 0 .../GmSwapBox/GmShiftBox/GmShiftBox.tsx | 18 +- .../GmShiftBox/getShiftAvailableMarkets.tsx | 12 +- .../GmShiftBox/useShiftAvailableMarkets.tsx | 36 ++- .../Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx | 7 +- .../useUpdateByQueryParams.tsx | 30 +- .../SyntheticsStateContextProvider.tsx | 16 +- .../synthetics/markets/useMarketTokensData.ts | 15 +- .../trade/useSortedPoolsWithIndexToken.ts | 112 ++++---- 16 files changed, 320 insertions(+), 259 deletions(-) rename src/components/Synthetics/GmSwap/GmSwapBox/{ => GmDepositWithdrawalBox}/useUpdateInputAmounts.tsx (100%) rename src/components/Synthetics/GmSwap/GmSwapBox/{ => GmDepositWithdrawalBox}/useUpdateTokens.tsx (100%) rename src/components/Synthetics/GmSwap/GmSwapBox/{GmDepositWithdrawalBox => }/useUpdateByQueryParams.tsx (85%) diff --git a/src/components/Button/Button.scss b/src/components/Button/Button.scss index 6d2d470465..f6144f2479 100644 --- a/src/components/Button/Button.scss +++ b/src/components/Button/Button.scss @@ -44,12 +44,12 @@ padding: 1.65rem 1.6rem; line-height: 1; - &:not([disabled]):hover { + &:not([disabled], .disabled):hover { background: var(--primary-btn-hover); box-shadow: 0 0 0.4rem 0.5rem color-mix(in srgb, var(--color-blue-600) 10%, transparent); } - &:not([disabled]):active { + &:not([disabled], .disabled):active { background-color: var(--primary-btn-active); box-shadow: 0 0 0.4rem 0.6rem color-mix(in srgb, var(--color-blue-600) 10%, transparent); } @@ -81,11 +81,11 @@ max-width: 0.8rem; } - &:not([disabled]):hover { + &:not([disabled], .disabled):hover { background-color: #50577e99; } - &:not([disabled]):active { + &:not([disabled], .disabled):active { background: #50577eb3; } } diff --git a/src/components/Button/ButtonLink.tsx b/src/components/Button/ButtonLink.tsx index 0ba7c41a6d..2157aa5e1e 100644 --- a/src/components/Button/ButtonLink.tsx +++ b/src/components/Button/ButtonLink.tsx @@ -14,6 +14,10 @@ type ButtonProps = { qa?: string; }; +function preventClick(e: React.MouseEvent) { + e.preventDefault(); +} + export default function ButtonLink({ className, to, @@ -47,7 +51,7 @@ export default function ButtonLink({ ); } return ( - + {children} ); diff --git a/src/components/Skeleton/GMListSkeletonStructure.tsx b/src/components/Skeleton/GMListSkeletonStructure.tsx index b0d7146889..c69d125d83 100644 --- a/src/components/Skeleton/GMListSkeletonStructure.tsx +++ b/src/components/Skeleton/GMListSkeletonStructure.tsx @@ -33,13 +33,7 @@ export default function GMListSkeletonStructure() { - + ); diff --git a/src/components/Synthetics/GmList/GmList.tsx b/src/components/Synthetics/GmList/GmList.tsx index afe5ac9b89..bdbf0777d8 100644 --- a/src/components/Synthetics/GmList/GmList.tsx +++ b/src/components/Synthetics/GmList/GmList.tsx @@ -14,6 +14,7 @@ import { useSelector } from "context/SyntheticsStateContext/utils"; import { MarketTokensAPRData, MarketsInfoData, + UserEarningsData, getMarketIndexName, getMarketPoolName, getMaxPoolUsd, @@ -24,7 +25,7 @@ import { } from "domain/synthetics/markets"; import { useDaysConsideredInMarketsApr } from "domain/synthetics/markets/useDaysConsideredInMarketsApr"; import { useUserEarnings } from "domain/synthetics/markets/useUserEarnings"; -import { TokensData, convertToUsd, getTokenData } from "domain/synthetics/tokens"; +import { TokenData, TokensData, convertToUsd, getTokenData } from "domain/synthetics/tokens"; import { formatTokenAmount, formatTokenAmountWithUsd, formatUsd, formatUsdPrice } from "lib/numbers"; import { getByKey } from "lib/objects"; import { sortGmTokensByField } from "./sortGmTokensByField"; @@ -43,6 +44,7 @@ import TokenIcon from "components/TokenIcon/TokenIcon"; import TooltipWithPortal from "components/Tooltip/TooltipWithPortal"; import GmAssetDropdown from "../GmAssetDropdown/GmAssetDropdown"; import { ExchangeTd, ExchangeTh, ExchangeTheadTr, ExchangeTr } from "../OrderList/ExchangeTable"; +import { useShiftAvailableMarkets } from "../GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableMarkets"; type Props = { marketsTokensApyData: MarketTokensAPRData | undefined; @@ -67,6 +69,11 @@ export function GmList({ marketsTokensApyData, marketsTokensIncentiveAprData, sh const daysConsidered = useDaysConsideredInMarketsApr(); const { orderBy, direction, getSorterProps } = useSorterHandlers(); const [searchText, setSearchText] = useState(""); + const shiftAvailableMarkets = useShiftAvailableMarkets(); + const shiftAvailableMarketAddressSet = useMemo( + () => new Set(shiftAvailableMarkets.map((m) => m.marketTokenAddress)), + [shiftAvailableMarkets] + ); const isLoading = !marketsInfoData || !marketTokensData; @@ -175,117 +182,21 @@ export function GmList({ marketsTokensApyData, marketsTokensIncentiveAprData, sh {currentData.length > 0 && - currentData.map((token) => { - const market = getByKey(marketsInfoData, token?.address)!; - - const indexToken = getTokenData(tokensData, market?.indexTokenAddress, "native"); - const longToken = getTokenData(tokensData, market?.longTokenAddress); - const shortToken = getTokenData(tokensData, market?.shortTokenAddress); - const mintableInfo = market && token ? getMintableMarketTokens(market, token) : undefined; - - const apy = getByKey(marketsTokensApyData, token?.address); - const incentiveApr = getByKey(marketsTokensIncentiveAprData, token?.address); - const marketEarnings = getByKey(userEarnings?.byMarketAddress, token?.address); - - if (!token || !indexToken || !longToken || !shortToken) { - return null; - } - - const totalSupply = token?.totalSupply; - const totalSupplyUsd = convertToUsd(totalSupply, token?.decimals, token?.prices?.minPrice); - const tokenIconName = market.isSpotOnly - ? getNormalizedTokenSymbol(longToken.symbol) + getNormalizedTokenSymbol(shortToken.symbol) - : getNormalizedTokenSymbol(indexToken.symbol); - - return ( - - -
-
- -
-
-
- {getMarketIndexName({ indexToken, isSpotOnly: market?.isSpotOnly })} - -
- -
-
-
- [{getMarketPoolName({ longToken, shortToken })}] -
-
-
- {showDebugValues && {market.marketTokenAddress}} -
- {formatUsdPrice(token.prices?.minPrice)} - - - {formatTokenAmount(totalSupply, token.decimals, "GM", { - useCommas: true, - displayDecimals: 2, - })} -
({formatUsd(totalSupplyUsd)}) -
- - - - - - - - - - - - - -
- - -
-
-
- ); - })} + currentData.map((token) => ( + + ))} {!currentData.length && !isLoading && ( @@ -411,6 +322,135 @@ function useFilterSortGmPools({ return filteredGmTokens; } +function GmListItem({ + marketsInfoData, + tokensData, + token, + marketsTokensApyData, + marketsTokensIncentiveAprData, + userEarnings, + showDebugValues, + daysConsidered, + shouldScrollToTop, + isShiftAvailable, +}: { + marketsInfoData: MarketsInfoData | undefined; + tokensData: TokensData | undefined; + token: TokenData; + marketsTokensApyData: MarketTokensAPRData | undefined; + marketsTokensIncentiveAprData: MarketTokensAPRData | undefined; + userEarnings: UserEarningsData | null; + showDebugValues: boolean; + daysConsidered: number; + shouldScrollToTop: boolean | undefined; + isShiftAvailable: boolean; +}) { + const market = getByKey(marketsInfoData, token?.address)!; + + const indexToken = getTokenData(tokensData, market?.indexTokenAddress, "native"); + const longToken = getTokenData(tokensData, market?.longTokenAddress); + const shortToken = getTokenData(tokensData, market?.shortTokenAddress); + const mintableInfo = market && token ? getMintableMarketTokens(market, token) : undefined; + + const apy = getByKey(marketsTokensApyData, token?.address); + const incentiveApr = getByKey(marketsTokensIncentiveAprData, token?.address); + const marketEarnings = getByKey(userEarnings?.byMarketAddress, token?.address); + + if (!token || !indexToken || !longToken || !shortToken) { + return null; + } + + const totalSupply = token?.totalSupply; + const totalSupplyUsd = convertToUsd(totalSupply, token?.decimals, token?.prices?.minPrice); + const tokenIconName = market.isSpotOnly + ? getNormalizedTokenSymbol(longToken.symbol) + getNormalizedTokenSymbol(shortToken.symbol) + : getNormalizedTokenSymbol(indexToken.symbol); + + return ( + + +
+
+ +
+
+
+ {getMarketIndexName({ indexToken, isSpotOnly: market?.isSpotOnly })} + +
+ +
+
+
+ [{getMarketPoolName({ longToken, shortToken })}] +
+
+
+ {showDebugValues && {market.marketTokenAddress}} +
+ {formatUsdPrice(token.prices?.minPrice)} + + + {formatTokenAmount(totalSupply, token.decimals, "GM", { + useCommas: true, + displayDecimals: 2, + })} +
({formatUsd(totalSupplyUsd)}) +
+ + + + + + + + + + + + + +
+ + + +
+
+
+ ); +} + function MintableAmount({ mintableInfo, market, diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx index 254012b877..b68851df98 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx @@ -46,11 +46,11 @@ import { useCallback, useEffect, useMemo } from "react"; import type { GmSwapBoxProps } from "../GmSwapBox"; import { showMarketToast } from "../showMarketToast"; import { Mode, Operation } from "../types"; -import { useUpdateInputAmounts } from "../useUpdateInputAmounts"; -import { useUpdateTokens } from "../useUpdateTokens"; +import { useUpdateInputAmounts } from "./useUpdateInputAmounts"; +import { useUpdateTokens } from "./useUpdateTokens"; import { useDepositWithdrawalAmounts } from "./useDepositWithdrawalAmounts"; import { useGmDepositWithdrawalBoxState } from "./useGmDepositWithdrawalBoxState"; -import { useUpdateByQueryParams } from "./useUpdateByQueryParams"; +import { useUpdateByQueryParams } from "../useUpdateByQueryParams"; import Button from "components/Button/Button"; import BuyInputSection from "components/BuyInputSection/BuyInputSection"; @@ -62,11 +62,10 @@ import { Swap } from "../Swap"; import { InfoRows } from "./InfoRows"; export function GmSwapBoxDepositWithdrawal(p: GmSwapBoxProps) { - const { selectedMarketAddress, operation, mode, onSetMode, onSetOperation, onSelectMarket } = p; + const { selectedMarketAddress: marketAddress, operation, mode, onSetMode, onSetOperation, onSelectMarket } = p; const isMetamaskMobile = useIsMetamaskMobile(); const { openConnectModal } = useConnectModal(); - const marketAddress = selectedMarketAddress; const { shouldDisableValidationForTesting } = useSettings(); const { chainId } = useChainId(); @@ -98,8 +97,6 @@ export function GmSwapBoxDepositWithdrawal(p: GmSwapBoxProps) { setStage, isHighPriceImpactAccepted, setIsHighPriceImpactAccepted, - indexName, - setIndexName, firstTokenAddress, setFirstTokenAddress, secondTokenAddress, @@ -124,6 +121,7 @@ export function GmSwapBoxDepositWithdrawal(p: GmSwapBoxProps) { const marketTokensData = isDeposit ? depositMarketTokensData : withdrawalMarketTokensData; const marketInfo = getByKey(marketsInfoData, marketAddress); + const indexName = marketInfo && getMarketIndexName(marketInfo); let firstToken = getTokenData(tokensData, firstTokenAddress); let firstTokenAmount = parseValue(firstTokenInputValue, firstToken?.decimals || 0); @@ -578,11 +576,10 @@ export function GmSwapBoxDepositWithdrawal(p: GmSwapBoxProps) { const marketTokenSelectMarket = useCallback( (marketInfo: MarketInfo): void => { - setIndexName(getMarketIndexName(marketInfo)); onMarketChange(marketInfo.marketTokenAddress); showMarketToast(marketInfo); }, - [onMarketChange, setIndexName] + [onMarketChange] ); // #endregion // #region Effects @@ -601,36 +598,18 @@ export function GmSwapBoxDepositWithdrawal(p: GmSwapBoxProps) { setSecondTokenInputValue, }); - useEffect( - function updateIndexToken() { - if (!indexName && sortedMarketsInfoByIndexToken.length) { - setIndexName(getMarketIndexName(sortedMarketsInfoByIndexToken[0])); - } - }, - [indexName, sortedMarketsInfoByIndexToken, setIndexName] - ); - useEffect( function updateMarket() { - const marketsByIndexName = sortedMarketsInfoByIndexToken.filter( - (market) => getMarketIndexName(market) === indexName - ); - - if (!marketsByIndexName.length) { - return; - } - - if (!marketAddress || !marketsByIndexName.find((market) => market.marketTokenAddress === marketAddress)) { - onMarketChange(marketsByIndexName[0].marketTokenAddress); + if (!marketAddress && sortedMarketsInfoByIndexToken.length) { + onMarketChange(sortedMarketsInfoByIndexToken[0].marketTokenAddress); } }, - [indexName, marketAddress, sortedMarketsInfoByIndexToken, onMarketChange] + [marketAddress, onMarketChange, sortedMarketsInfoByIndexToken] ); useUpdateByQueryParams({ setOperation: onSetOperation, setMode: onSetMode, - setIndexName, onSelectMarket, setFirstTokenAddress, }); @@ -732,6 +711,7 @@ export function GmSwapBoxDepositWithdrawal(p: GmSwapBoxProps) { markets={sortedMarketsInfoByIndexToken} marketTokensData={marketTokensData} isSideMenu + showAllPools showBalances showIndexIcon onSelectMarket={marketTokenSelectMarket} diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useGmDepositWithdrawalBoxState.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useGmDepositWithdrawalBoxState.tsx index dba2ec3634..95ea3f338f 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useGmDepositWithdrawalBoxState.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useGmDepositWithdrawalBoxState.tsx @@ -16,10 +16,6 @@ export function useGmDepositWithdrawalBoxState(operation: Operation, mode: Mode, const [focusedInput, setFocusedInput] = useState<"longCollateral" | "shortCollateral" | "market">("market"); const [stage, setStage] = useState<"swap" | "confirmation" | "processing">(); const [isHighPriceImpactAccepted, setIsHighPriceImpactAccepted] = useState(false); - const [indexName, setIndexName] = useLocalStorageSerializeKey( - getSyntheticsDepositIndexTokenKey(chainId), - undefined - ); const [firstTokenAddress, setFirstTokenAddress] = useLocalStorageSerializeKey( [chainId, SYNTHETICS_MARKET_DEPOSIT_TOKEN_KEY, isDeposit, marketAddress, "first"], undefined @@ -42,9 +38,6 @@ export function useGmDepositWithdrawalBoxState(operation: Operation, mode: Mode, isHighPriceImpactAccepted, setIsHighPriceImpactAccepted, - indexName, - setIndexName, - firstTokenAddress, setFirstTokenAddress, diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/useUpdateInputAmounts.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useUpdateInputAmounts.tsx similarity index 100% rename from src/components/Synthetics/GmSwap/GmSwapBox/useUpdateInputAmounts.tsx rename to src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useUpdateInputAmounts.tsx diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/useUpdateTokens.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useUpdateTokens.tsx similarity index 100% rename from src/components/Synthetics/GmSwap/GmSwapBox/useUpdateTokens.tsx rename to src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useUpdateTokens.tsx diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx index ca4dd40a9b..a613113880 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx @@ -1,6 +1,6 @@ import { t } from "@lingui/macro"; import { useConnectModal } from "@rainbow-me/rainbowkit"; -import { useCallback, useEffect, useMemo, useState } from "react"; +import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react"; import { HIGH_PRICE_IMPACT_BPS } from "config/factors"; import { useSettings } from "context/SettingsContext/SettingsContextProvider"; @@ -31,7 +31,8 @@ import { getCommonError, getGmShiftError } from "domain/synthetics/trade/utils/v import { bigMath } from "lib/bigmath"; import { formatAmountFree, formatTokenAmount, formatUsd, parseValue } from "lib/numbers"; import { getByKey } from "lib/objects"; -import { Operation } from "../types"; +import { Mode, Operation } from "../types"; +import { useUpdateByQueryParams } from "../useUpdateByQueryParams"; import { useShiftAvailableMarkets } from "./useShiftAvailableMarkets"; import { useShiftAvailableRelatedMarkets } from "./useShiftAvailableRelatedMarkets"; import { useUpdateMarkets } from "./useUpdateMarkets"; @@ -48,9 +49,14 @@ import { Swap } from "../Swap"; export function GmShiftBox({ selectedMarketAddress, onSelectMarket, + + onSetMode, + onSetOperation, }: { selectedMarketAddress: string | undefined; onSelectMarket: (marketAddress: string) => void; + onSetMode: Dispatch>; + onSetOperation: Dispatch>; }) { const [toMarketAddress, setToMarketAddress] = useState(undefined); const [selectedMarketText, setSelectedMarketText] = useState(""); @@ -72,7 +78,7 @@ export function GmShiftBox({ marketsInfoData, depositMarketTokensData ); - const shiftAvailableMarkets = useShiftAvailableMarkets(sortedMarketsInfoByIndexToken); + const shiftAvailableMarkets = useShiftAvailableMarkets(); const shiftAvailableRelatedMarkets = useShiftAvailableRelatedMarkets( marketsInfoData, sortedMarketsInfoByIndexToken, @@ -258,6 +264,12 @@ export function GmShiftBox({ [amounts, focusedInput, selectedToken, toToken] ); + useUpdateByQueryParams({ + onSelectMarket, + setMode: onSetMode, + setOperation: onSetOperation, + }); + const handleFormSubmit = useCallback( (event: React.FormEvent) => { event.preventDefault(); diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/getShiftAvailableMarkets.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/getShiftAvailableMarkets.tsx index 7eb1131810..e2e159514a 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/getShiftAvailableMarkets.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/getShiftAvailableMarkets.tsx @@ -1,18 +1,14 @@ import type { MarketInfo } from "domain/synthetics/markets/types"; import { EMPTY_ARRAY } from "lib/objects"; -export function getShiftAvailableMarkets({ - sortedMarketsInfoByIndexToken, -}: { - sortedMarketsInfoByIndexToken: MarketInfo[]; -}): MarketInfo[] { - if (sortedMarketsInfoByIndexToken.length === 0) { +export function getShiftAvailableMarkets({ markets }: { markets: MarketInfo[] }): MarketInfo[] { + if (markets.length === 0) { return EMPTY_ARRAY; } const shiftGroups: { [longShortKey: string]: MarketInfo[] } = {}; - for (const marketInfo of sortedMarketsInfoByIndexToken) { + for (const marketInfo of markets) { const longShortKey = `${marketInfo.longTokenAddress}-${marketInfo.shortTokenAddress}`; if (!shiftGroups[longShortKey]) { @@ -24,7 +20,7 @@ export function getShiftAvailableMarkets({ const availableMarkets: MarketInfo[] = []; - for (const marketInfo of sortedMarketsInfoByIndexToken) { + for (const marketInfo of markets) { const longShortKey = `${marketInfo.longTokenAddress}-${marketInfo.shortTokenAddress}`; if (shiftGroups[longShortKey].length > 1) { diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableMarkets.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableMarkets.tsx index d73bbf92a6..ace398b6ad 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableMarkets.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableMarkets.tsx @@ -1,17 +1,33 @@ -import { useMemo } from "react"; - +import type { SyntheticsState } from "context/SyntheticsStateContext/SyntheticsStateContextProvider"; +import { selectMarketsInfoData } from "context/SyntheticsStateContext/selectors/globalSelectors"; +import { createSelector, useSelector } from "context/SyntheticsStateContext/utils"; import type { MarketInfo } from "domain/synthetics/markets/types"; +import { sortMarketsWithIndexToken } from "domain/synthetics/trade/useSortedPoolsWithIndexToken"; import { getShiftAvailableMarkets } from "./getShiftAvailableMarkets"; -export function useShiftAvailableMarkets(sortedMarketsInfoByIndexToken: MarketInfo[]) { - const shiftAvailableMarkets: MarketInfo[] = useMemo( - () => - getShiftAvailableMarkets({ - sortedMarketsInfoByIndexToken, - }), - [sortedMarketsInfoByIndexToken] - ); +const selectMarketTokensForDepositData = (s: SyntheticsState) => s.globals.depositMarketTokensData; + +const selectSortedMarketInfoByIndexToken = createSelector((q) => { + const marketsInfoData = q(selectMarketsInfoData); + + const depositMarketTokensData = q(selectMarketTokensForDepositData); + + const sortedMarketsInfoByIndexToken = sortMarketsWithIndexToken(marketsInfoData, depositMarketTokensData); + + return sortedMarketsInfoByIndexToken.marketsInfo; +}); + +const selectShiftAvailableMarkets = createSelector((q) => { + const sortedMarketsInfoByIndexToken = q(selectSortedMarketInfoByIndexToken); + + return getShiftAvailableMarkets({ + markets: sortedMarketsInfoByIndexToken, + }); +}); + +export function useShiftAvailableMarkets() { + const shiftAvailableMarkets: MarketInfo[] = useSelector(selectShiftAvailableMarkets); return shiftAvailableMarkets; } diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx index e374f6989e..384f714f02 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx @@ -82,7 +82,12 @@ export function GmSwapBox(p: GmSwapBoxProps) { onSetOperation={onSetOperation} /> ) : ( - + )}
); diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useUpdateByQueryParams.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/useUpdateByQueryParams.tsx similarity index 85% rename from src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useUpdateByQueryParams.tsx rename to src/components/Synthetics/GmSwap/GmSwapBox/useUpdateByQueryParams.tsx index 81d53199d0..2bf666db47 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useUpdateByQueryParams.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/useUpdateByQueryParams.tsx @@ -1,7 +1,7 @@ import { Trans } from "@lingui/macro"; import { isAddress } from "ethers"; import { values } from "lodash"; -import { useEffect } from "react"; +import { useEffect, useMemo } from "react"; import { useHistory } from "react-router-dom"; import { convertTokenAddress, getTokenBySymbolSafe } from "config/tokens"; @@ -11,8 +11,7 @@ import { getMarketIndexName, getMarketPoolName } from "domain/synthetics/markets import { helperToast } from "lib/helperToast"; import { getMatchingValueFromObject } from "lib/objects"; import useSearchParams from "lib/useSearchParams"; - -import { Mode, Operation } from "../types"; +import { Mode, Operation } from "./types"; type SearchParams = { market?: string; @@ -27,20 +26,19 @@ export function useUpdateByQueryParams({ setOperation, setMode, setFirstTokenAddress, - setIndexName, onSelectMarket, }: { setOperation: (operation: Operation) => void; setMode: (mode: Mode) => void; - setFirstTokenAddress: (address: string | undefined) => void; - setIndexName: (indexName: string | undefined) => void; + setFirstTokenAddress?: (address: string | undefined) => void; onSelectMarket: (marketAddress: string) => void; }) { const history = useHistory(); const searchParams = useSearchParams(); const chainId = useSelector(selectChainId); - const markets = values(useSelector(selectMarketsInfoData)); + const marketsInfo = useSelector(selectMarketsInfoData); + const markets = useMemo(() => values(marketsInfo), [marketsInfo]); useEffect( function updateByQueryParams() { @@ -54,6 +52,8 @@ export function useUpdateByQueryParams({ finalOperation = Operation.Deposit; } else if (operation.toLowerCase() === "sell") { finalOperation = Operation.Withdrawal; + } else if (operation.toLowerCase() === "shift") { + finalOperation = Operation.Shift; } if (finalOperation) { @@ -68,7 +68,7 @@ export function useUpdateByQueryParams({ } } - if (fromToken) { + if (fromToken && setFirstTokenAddress) { const fromTokenInfo = getTokenBySymbolSafe(chainId, fromToken, { version: "v2", }); @@ -85,7 +85,6 @@ export function useUpdateByQueryParams({ if (marketAddress && isAddress(marketAddress)) { const marketInfo = markets.find((market) => market.marketTokenAddress.toLowerCase() === marketAddress); if (marketInfo) { - setIndexName(getMarketIndexName(marketInfo)); onSelectMarket(marketInfo.marketTokenAddress); const indexName = getMarketIndexName(marketInfo); const poolName = getMarketPoolName(marketInfo); @@ -112,17 +111,6 @@ export function useUpdateByQueryParams({ } } }, - [ - // - history, - onSelectMarket, - searchParams, - setIndexName, - setOperation, - setMode, - setFirstTokenAddress, - chainId, - markets, - ] + [history, onSelectMarket, searchParams, setOperation, setMode, setFirstTokenAddress, chainId, markets, marketsInfo] ); } diff --git a/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx b/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx index 2eb82a79b1..5050c7925c 100644 --- a/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx +++ b/src/context/SyntheticsStateContext/SyntheticsStateContextProvider.tsx @@ -5,7 +5,13 @@ import { PeriodAccountStats, usePeriodAccountStats } from "domain/synthetics/acc import { useGasLimits, useGasPrice } from "domain/synthetics/fees"; import { RebateInfoItem, useRebatesInfoRequest } from "domain/synthetics/fees/useRebatesInfo"; import useUiFeeFactor from "domain/synthetics/fees/utils/useUiFeeFactor"; -import { MarketsInfoResult, MarketsResult, useMarkets, useMarketsInfoRequest } from "domain/synthetics/markets"; +import { + MarketsInfoResult, + MarketsResult, + useMarketTokensDataRequest, + useMarkets, + useMarketsInfoRequest, +} from "domain/synthetics/markets"; import { OrderEditorState, useOrderEditorState } from "domain/synthetics/orders/useOrderEditorState"; import { AggregatedOrdersDataResult, useOrdersInfoRequest } from "domain/synthetics/orders/useOrdersInfo"; import { @@ -14,6 +20,7 @@ import { usePositionsConstantsRequest, usePositionsInfoRequest, } from "domain/synthetics/positions"; +import { TokensData } 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"; @@ -51,6 +58,7 @@ export type SyntheticsState = { positionsConstants: PositionsConstantsResult; uiFeeFactor: bigint; userReferralInfo: UserReferralInfo | undefined; + depositMarketTokensData: TokensData | undefined; closingPositionKey: string | undefined; setClosingPositionKey: (key: string | undefined) => void; @@ -112,6 +120,10 @@ export function SyntheticsStateContextProvider({ const markets = useMarkets(chainId); const marketsInfo = useMarketsInfoRequest(chainId); + const { marketTokensData: depositMarketTokensData } = useMarketTokensDataRequest(chainId, { + isDeposit: true, + account, + }); const positionsConstants = usePositionsConstantsRequest(chainId); const uiFeeFactor = useUiFeeFactor(chainId); const userReferralInfo = useUserReferralInfoRequest(signer, chainId, account, skipLocalReferralCode); @@ -181,6 +193,7 @@ export function SyntheticsStateContextProvider({ }, uiFeeFactor, userReferralInfo, + depositMarketTokensData, closingPositionKey, setClosingPositionKey, @@ -230,6 +243,7 @@ export function SyntheticsStateContextProvider({ positionSellerState, positionEditorState, confirmationBoxState, + depositMarketTokensData, ]); latestState = state; diff --git a/src/domain/synthetics/markets/useMarketTokensData.ts b/src/domain/synthetics/markets/useMarketTokensData.ts index a3dd27e83a..c72ceb75db 100644 --- a/src/domain/synthetics/markets/useMarketTokensData.ts +++ b/src/domain/synthetics/markets/useMarketTokensData.ts @@ -18,9 +18,11 @@ type MarketTokensDataResult = { marketTokensData?: TokensData; }; -export function useMarketTokensData(chainId: number, p: { isDeposit: boolean }): MarketTokensDataResult { - const { isDeposit } = p; - const account = useSelector((s) => s.globals.account); +export function useMarketTokensDataRequest( + chainId: number, + p: { isDeposit: boolean; account?: string } +): MarketTokensDataResult { + const { isDeposit, account } = p; const { tokensData, pricesUpdatedAt } = useTokensDataRequest(chainId); const { marketsData, marketsAddresses } = useMarkets(chainId); @@ -137,3 +139,10 @@ export function useMarketTokensData(chainId: number, p: { isDeposit: boolean }): marketTokensData: data, }; } + +export function useMarketTokensData(chainId: number, p: { isDeposit: boolean }): MarketTokensDataResult { + const { isDeposit } = p; + const account = useSelector((s) => s.globals.account); + + return useMarketTokensDataRequest(chainId, { isDeposit, account }); +} diff --git a/src/domain/synthetics/trade/useSortedPoolsWithIndexToken.ts b/src/domain/synthetics/trade/useSortedPoolsWithIndexToken.ts index 127f7a5940..2033dc51f0 100644 --- a/src/domain/synthetics/trade/useSortedPoolsWithIndexToken.ts +++ b/src/domain/synthetics/trade/useSortedPoolsWithIndexToken.ts @@ -1,66 +1,76 @@ -import { getByKey } from "lib/objects"; +import { EMPTY_ARRAY, getByKey } from "lib/objects"; import groupBy from "lodash/groupBy"; import { useMemo } from "react"; import { MarketInfo, MarketsInfoData } from "../markets"; import { TokenData, TokensData, convertToUsd } from "../tokens"; -function useSortedPoolsWithIndexToken(marketsInfoData?: MarketsInfoData, marketTokensData?: TokensData) { - const sortedMarketsWithIndexToken = useMemo(() => { - if (!marketsInfoData || !marketTokensData) { - return { - markets: [], - marketsInfo: [], - }; - } - // Group markets by index token address - const groupedMarketList: { [marketAddress: string]: MarketInfo[] } = groupBy( - Object.values(marketsInfoData), - (market) => market[market.isSpotOnly ? "marketTokenAddress" : "indexTokenAddress"] - ); +const DEFAULT_VALUE = { + markets: EMPTY_ARRAY, + marketsInfo: EMPTY_ARRAY, +}; - const allMarkets = Object.values(groupedMarketList) - .map((markets) => { - return markets - .filter((market) => { - const marketInfoData = getByKey(marketsInfoData, market.marketTokenAddress)!; - return !marketInfoData.isDisabled; - }) - .map((market) => getByKey(marketTokensData, market.marketTokenAddress)!); - }) - .filter((markets) => markets.length > 0); +export function sortMarketsWithIndexToken( + marketsInfoData: MarketsInfoData | undefined, + marketTokensData: TokensData | undefined +) { + if (!marketsInfoData || !marketTokensData) { + return DEFAULT_VALUE; + } + // Group markets by index token address + const groupedMarketList: { [marketAddress: string]: MarketInfo[] } = groupBy( + Object.values(marketsInfoData), + (market) => market[market.isSpotOnly ? "marketTokenAddress" : "indexTokenAddress"] + ); - const sortedGroups = allMarkets!.sort((a, b) => { - const totalMarketSupplyA = a.reduce((acc, market) => { - const totalSupplyUsd = convertToUsd(market?.totalSupply, market?.decimals, market?.prices.minPrice); - acc = acc + (totalSupplyUsd ?? 0n); - return acc; - }, 0n); + const allMarkets = Object.values(groupedMarketList) + .map((markets) => { + return markets + .filter((market) => { + const marketInfoData = getByKey(marketsInfoData, market.marketTokenAddress)!; + return !marketInfoData.isDisabled; + }) + .map((market) => getByKey(marketTokensData, market.marketTokenAddress)!); + }) + .filter((markets) => markets.length > 0); - const totalMarketSupplyB = b.reduce((acc, market) => { - const totalSupplyUsd = convertToUsd(market?.totalSupply, market?.decimals, market?.prices.minPrice); - acc = acc + (totalSupplyUsd ?? 0n); - return acc; - }, 0n); + const sortedGroups = allMarkets!.sort((a, b) => { + const totalMarketSupplyA = a.reduce((acc, market) => { + const totalSupplyUsd = convertToUsd(market?.totalSupply, market?.decimals, market?.prices.minPrice); + acc = acc + (totalSupplyUsd ?? 0n); + return acc; + }, 0n); - return totalMarketSupplyA > totalMarketSupplyB ? -1 : 1; - }); + const totalMarketSupplyB = b.reduce((acc, market) => { + const totalSupplyUsd = convertToUsd(market?.totalSupply, market?.decimals, market?.prices.minPrice); + acc = acc + (totalSupplyUsd ?? 0n); + return acc; + }, 0n); + + return totalMarketSupplyA > totalMarketSupplyB ? -1 : 1; + }); - // Sort markets within each group by total supply - const sortedMarkets = sortedGroups.map((markets) => { - return markets.sort((a, b) => { - const totalSupplyUsdA = convertToUsd(a.totalSupply, a.decimals, a.prices.minPrice)!; - const totalSupplyUsdB = convertToUsd(b.totalSupply, b.decimals, b.prices.minPrice)!; - return totalSupplyUsdA > totalSupplyUsdB ? -1 : 1; - }); + // Sort markets within each group by total supply + const sortedMarkets = sortedGroups.map((markets) => { + return markets.sort((a, b) => { + const totalSupplyUsdA = convertToUsd(a.totalSupply, a.decimals, a.prices.minPrice)!; + const totalSupplyUsdB = convertToUsd(b.totalSupply, b.decimals, b.prices.minPrice)!; + return totalSupplyUsdA > totalSupplyUsdB ? -1 : 1; }); + }); - // Flatten the sorted markets array - const flattenedMarkets = sortedMarkets.flat(Infinity).filter(Boolean) as TokenData[]; - return { - markets: flattenedMarkets, - marketsInfo: flattenedMarkets.map((market) => getByKey(marketsInfoData, market.address)!), - }; - }, [marketsInfoData, marketTokensData]); + // Flatten the sorted markets array + const flattenedMarkets = sortedMarkets.flat(Infinity).filter(Boolean) as TokenData[]; + return { + markets: flattenedMarkets, + marketsInfo: flattenedMarkets.map((market) => getByKey(marketsInfoData, market.address)!), + }; +} + +function useSortedPoolsWithIndexToken(marketsInfoData?: MarketsInfoData, marketTokensData?: TokensData) { + const sortedMarketsWithIndexToken = useMemo( + () => sortMarketsWithIndexToken(marketsInfoData, marketTokensData), + [marketsInfoData, marketTokensData] + ); return sortedMarketsWithIndexToken; } From f96d5ea89513312be01728b5d1c3f9da7a59b9d2 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 6 Aug 2024 11:15:44 +0000 Subject: [PATCH 22/62] Enhance shift error validation --- .../GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx | 4 ++-- src/domain/synthetics/markets/utils.ts | 2 +- src/domain/synthetics/trade/utils/validation.ts | 11 +++++++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx index a613113880..33d768c757 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx @@ -179,7 +179,7 @@ export function GmShiftBox({ hasOutdatedUi, })[0]; - const swapError = getGmShiftError({ + const shiftError = getGmShiftError({ fromMarketInfo: selectedMarketInfo, fromToken: selectedToken, fromTokenAmount: amounts?.fromTokenAmount, @@ -193,7 +193,7 @@ export function GmShiftBox({ priceImpactUsd: amounts?.swapPriceImpactDeltaUsd, })[0]; - const error = commonError || swapError; + const error = commonError || shiftError; const onSubmit = () => { setIsConfirmationBoxVisible(true); diff --git a/src/domain/synthetics/markets/utils.ts b/src/domain/synthetics/markets/utils.ts index 771268384c..82d2e0c958 100644 --- a/src/domain/synthetics/markets/utils.ts +++ b/src/domain/synthetics/markets/utils.ts @@ -376,7 +376,7 @@ export function getSellableMarketToken(marketInfo: MarketInfo, marketToken: Toke return { maxLongSellableUsd: 0n, maxShortSellableUsd: 0n, - total: 0n, + totalAmount: 0n, }; } diff --git a/src/domain/synthetics/trade/utils/validation.ts b/src/domain/synthetics/trade/utils/validation.ts index a59f25fdd8..882f5d8c2b 100644 --- a/src/domain/synthetics/trade/utils/validation.ts +++ b/src/domain/synthetics/trade/utils/validation.ts @@ -6,6 +6,7 @@ import { getMaxAllowedLeverageByMinCollateralFactor, getMintableMarketTokens, getOpenInterestUsd, + getSellableMarketToken, } from "domain/synthetics/markets"; import { PositionInfo, willPositionCollateralBeSufficientForPosition } from "domain/synthetics/positions"; import { TokenData, TokensRatio } from "domain/synthetics/tokens"; @@ -696,14 +697,20 @@ export function getGmShiftError({ return [t`Max pool USD exceeded`]; } - const totalCollateralUsd = fromTokenUsd ?? 0n; + const sellable = getSellableMarketToken(fromMarketInfo, fromToken); + + if (fromTokenAmount !== undefined && sellable.totalAmount < fromTokenAmount) { + return [t`Max ${fromToken?.symbol} sellable amount exceeded`]; + } const mintableInfo = getMintableMarketTokens(toMarketInfo, toToken); if (toTokenAmount !== undefined && toTokenAmount > mintableInfo.mintableAmount) { - return [t`Max ${toToken?.symbol} amount exceeded`]; + return [t`Max ${toToken?.symbol} buyable amount exceeded`]; } + const totalCollateralUsd = fromTokenUsd ?? 0n; + const feesExistAndNegative = fees?.totalFees?.deltaUsd === undefined ? undefined : fees?.totalFees?.deltaUsd < 0; if (feesExistAndNegative && bigMath.abs(fees?.totalFees?.deltaUsd ?? 0n) > totalCollateralUsd) { return [t`Fees exceed Pay amount`]; From d94d60cbd4b52cc433005fea4c3ff0bf13d3504a Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 6 Aug 2024 11:16:14 +0000 Subject: [PATCH 23/62] Sort imports in useGmDepositWithdrawalBoxState.tsx --- .../useGmDepositWithdrawalBoxState.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useGmDepositWithdrawalBoxState.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useGmDepositWithdrawalBoxState.tsx index 95ea3f338f..12df713b79 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useGmDepositWithdrawalBoxState.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/useGmDepositWithdrawalBoxState.tsx @@ -1,12 +1,12 @@ import { useState } from "react"; -import { getSyntheticsDepositIndexTokenKey, SYNTHETICS_MARKET_DEPOSIT_TOKEN_KEY } from "config/localStorage"; +import { SYNTHETICS_MARKET_DEPOSIT_TOKEN_KEY } from "config/localStorage"; +import { selectChainId } from "context/SyntheticsStateContext/selectors/globalSelectors"; +import { useSelector } from "context/SyntheticsStateContext/utils"; import { useLocalStorageSerializeKey } from "lib/localStorage"; import { useSafeState } from "lib/useSafeState"; -import { useSelector } from "context/SyntheticsStateContext/utils"; -import { selectChainId } from "context/SyntheticsStateContext/selectors/globalSelectors"; -import { Operation, Mode } from "../types"; +import { Mode, Operation } from "../types"; export function useGmDepositWithdrawalBoxState(operation: Operation, mode: Mode, marketAddress: string | undefined) { const isDeposit = operation === Operation.Deposit; From 9c46de65d9b164b00129fb2814be1f4acebc8374 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 6 Aug 2024 12:11:42 +0000 Subject: [PATCH 24/62] Shift validation errors --- .../GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx | 4 ++++ src/domain/synthetics/trade/utils/shift.ts | 10 ++++++++++ src/domain/synthetics/trade/utils/validation.ts | 13 +++++++++++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx index 33d768c757..e12f6144a9 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx @@ -184,6 +184,8 @@ export function GmShiftBox({ fromToken: selectedToken, fromTokenAmount: amounts?.fromTokenAmount, fromTokenUsd: amounts?.fromTokenUsd, + fromLongTokenAmount: amounts?.fromLongTokenAmount, + fromShortTokenAmount: amounts?.fromShortTokenAmount, toMarketInfo: toMarketInfo, toToken: toToken, toTokenAmount: amounts?.toTokenAmount, @@ -220,6 +222,8 @@ export function GmShiftBox({ selectedToken, amounts?.fromTokenAmount, amounts?.fromTokenUsd, + amounts?.fromLongTokenAmount, + amounts?.fromShortTokenAmount, amounts?.toTokenAmount, amounts?.swapPriceImpactDeltaUsd, toMarketInfo, diff --git a/src/domain/synthetics/trade/utils/shift.ts b/src/domain/synthetics/trade/utils/shift.ts index 7cfa1974f9..03c3d93ea6 100644 --- a/src/domain/synthetics/trade/utils/shift.ts +++ b/src/domain/synthetics/trade/utils/shift.ts @@ -8,6 +8,8 @@ import { getWithdrawalAmounts } from "./withdrawal"; type ShiftAmounts = { fromTokenAmount: bigint; fromTokenUsd: bigint; + fromLongTokenAmount: bigint; + fromShortTokenAmount: bigint; toTokenAmount: bigint; toTokenUsd: bigint; uiFeeUsd: bigint; @@ -36,6 +38,8 @@ export function getShiftAmounts({ const values: ShiftAmounts = { fromTokenAmount: 0n, fromTokenUsd: 0n, + fromLongTokenAmount: 0n, + fromShortTokenAmount: 0n, toTokenAmount: 0n, toTokenUsd: 0n, uiFeeUsd: 0n, @@ -74,6 +78,9 @@ export function getShiftAmounts({ forShift: true, }); + values.fromLongTokenAmount = withdrawalAmounts.longTokenAmount; + values.fromShortTokenAmount = withdrawalAmounts.shortTokenAmount; + values.uiFeeUsd = withdrawalAmounts.uiFeeUsd; values.swapPriceImpactDeltaUsd = depositAmounts.swapPriceImpactDeltaUsd; @@ -109,6 +116,9 @@ export function getShiftAmounts({ forShift: true, }); + values.fromLongTokenAmount = depositAmounts.longTokenAmount; + values.fromShortTokenAmount = depositAmounts.shortTokenAmount; + values.uiFeeUsd = withdrawalAmounts.uiFeeUsd; values.swapPriceImpactDeltaUsd = depositAmounts.swapPriceImpactDeltaUsd; diff --git a/src/domain/synthetics/trade/utils/validation.ts b/src/domain/synthetics/trade/utils/validation.ts index 882f5d8c2b..009a39b035 100644 --- a/src/domain/synthetics/trade/utils/validation.ts +++ b/src/domain/synthetics/trade/utils/validation.ts @@ -656,6 +656,8 @@ export function getGmShiftError({ fromToken, fromTokenAmount, fromTokenUsd, + fromLongTokenAmount, + fromShortTokenAmount, toMarketInfo, toToken, toTokenAmount, @@ -668,6 +670,8 @@ export function getGmShiftError({ fromToken: TokenData | undefined; fromTokenAmount: bigint | undefined; fromTokenUsd: bigint | undefined; + fromLongTokenAmount: bigint | undefined; + fromShortTokenAmount: bigint | undefined; toMarketInfo: MarketInfo | undefined; toToken: TokenData | undefined; toTokenAmount: bigint | undefined; @@ -705,8 +709,13 @@ export function getGmShiftError({ const mintableInfo = getMintableMarketTokens(toMarketInfo, toToken); - if (toTokenAmount !== undefined && toTokenAmount > mintableInfo.mintableAmount) { - return [t`Max ${toToken?.symbol} buyable amount exceeded`]; + const longExceedCapacity = + fromLongTokenAmount !== undefined && fromLongTokenAmount > mintableInfo.longDepositCapacityAmount; + const shortExceedCapacity = + fromShortTokenAmount !== undefined && fromShortTokenAmount > mintableInfo.shortDepositCapacityAmount; + + if (longExceedCapacity || shortExceedCapacity) { + return [t`Max ${fromToken?.symbol} buyable amount exceeded`]; } const totalCollateralUsd = fromTokenUsd ?? 0n; From 6d833d06c2aeff1efb1f5444e19517fd03e612f3 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 6 Aug 2024 13:28:49 +0000 Subject: [PATCH 25/62] FIx deposit amounts --- src/domain/synthetics/trade/utils/deposit.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/domain/synthetics/trade/utils/deposit.ts b/src/domain/synthetics/trade/utils/deposit.ts index e29f8c7cac..dcc07513ad 100644 --- a/src/domain/synthetics/trade/utils/deposit.ts +++ b/src/domain/synthetics/trade/utils/deposit.ts @@ -136,8 +136,8 @@ export function getDepositAmounts(p: { if (p.forShift) { // Reverse the withdrawal amounts - values.longTokenUsd = bigMath.mulDiv(values.marketTokenUsd, shortPoolUsd, totalPoolUsd); - values.shortTokenUsd = bigMath.mulDiv(values.marketTokenUsd, longPoolUsd, totalPoolUsd); + values.longTokenUsd = bigMath.mulDiv(values.marketTokenUsd, longPoolUsd, totalPoolUsd); + values.shortTokenUsd = bigMath.mulDiv(values.marketTokenUsd, shortPoolUsd, totalPoolUsd); } else if (includeLongToken && includeShortToken && prevSumUsd > 0) { values.longTokenUsd = bigMath.mulDiv(values.marketTokenUsd, prevLongTokenUsd, prevSumUsd); values.shortTokenUsd = values.marketTokenUsd - values.longTokenUsd; From b98447d0eab793928a211c9037636b00e730fdbc Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 6 Aug 2024 13:42:14 +0000 Subject: [PATCH 26/62] Fix gm list actions column --- src/components/Synthetics/OrderList/ExchangeTable.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/Synthetics/OrderList/ExchangeTable.tsx b/src/components/Synthetics/OrderList/ExchangeTable.tsx index e42b27590d..0a216fc0c1 100644 --- a/src/components/Synthetics/OrderList/ExchangeTable.tsx +++ b/src/components/Synthetics/OrderList/ExchangeTable.tsx @@ -44,5 +44,13 @@ export const ExchangeTr = forwardRef< ); }); export function ExchangeTd(props: PropsWithChildren & React.HTMLProps) { - return ; + return ( + + ); } From 7c54efcde590b4d64af9ef02600402c628bb216d Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 6 Aug 2024 14:32:37 +0000 Subject: [PATCH 27/62] gm shifts price impact acknowledgement --- .../GmSwapBox/GmShiftBox/GmShiftBox.tsx | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx index e12f6144a9..e91784ee3e 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx @@ -44,6 +44,7 @@ import { PoolSelector } from "components/MarketSelector/PoolSelector"; import { NetworkFeeRow } from "components/Synthetics/NetworkFeeRow/NetworkFeeRow"; import { GmConfirmationBox } from "../../GmConfirmationBox/GmConfirmationBox"; import { GmFees } from "../../GmFees/GmFees"; +import { HighPriceImpactRow } from "../HighPriceImpactRow"; import { Swap } from "../Swap"; export function GmShiftBox({ @@ -64,6 +65,7 @@ export function GmShiftBox({ const gmTokenFavoritesContext = useGmTokensFavorites(); const [focusedInput, setFocusedInput] = useState<"selectedMarket" | "toMarket" | undefined>(undefined); const [isConfirmationBoxVisible, setIsConfirmationBoxVisible] = useState(false); + const [isHighPriceImpactAccepted, setIsHighPriceImpactAccepted] = useState(false); const { openConnectModal } = useConnectModal(); const chainId = useSelector(selectChainId); @@ -191,7 +193,7 @@ export function GmShiftBox({ toTokenAmount: amounts?.toTokenAmount, fees, isHighPriceImpact: isHighPriceImpact, - isHighPriceImpactAccepted: true, + isHighPriceImpactAccepted, priceImpactUsd: amounts?.swapPriceImpactDeltaUsd, })[0]; @@ -230,6 +232,7 @@ export function GmShiftBox({ toToken, fees, isHighPriceImpact, + isHighPriceImpactAccepted, openConnectModal, shouldDisableValidationForTesting, ]); @@ -367,7 +370,7 @@ export function GmShiftBox({ {...gmTokenFavoritesContext} />
- + + {isHighPriceImpact && ( + + )} -
- -
+ + Date: Tue, 6 Aug 2024 14:37:01 +0000 Subject: [PATCH 28/62] Refactor GmShiftBox to handle parsing errors in fromTokenAmount and toTokenAmount --- .../GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx index e91784ee3e..201dbe7baa 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx @@ -101,9 +101,19 @@ export function GmShiftBox({ return; } - let fromTokenAmount = parseValue(selectedMarketText, selectedToken.decimals) ?? 0n; + let fromTokenAmount = 0n; + try { + fromTokenAmount = parseValue(selectedMarketText, selectedToken.decimals) ?? 0n; + } catch { + // pass + } - let toTokenAmount = parseValue(toMarketText, toToken.decimals) ?? 0n; + let toTokenAmount = 0n; + try { + toTokenAmount = parseValue(toMarketText, toToken.decimals) ?? 0n; + } catch { + // pass + } const amounts = getShiftAmounts({ fromMarketInfo: selectedMarketInfo, From b4bc3c7f12c0e2d2e353cdb4f14c07e9f61c608f Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 6 Aug 2024 14:43:29 +0000 Subject: [PATCH 29/62] Sync translations --- src/locales/de/messages.po | 124 ++++++++++++++++++++++++++------- src/locales/en/messages.po | 124 ++++++++++++++++++++++++++------- src/locales/es/messages.po | 124 ++++++++++++++++++++++++++------- src/locales/fr/messages.po | 124 ++++++++++++++++++++++++++------- src/locales/ja/messages.po | 124 ++++++++++++++++++++++++++------- src/locales/ko/messages.po | 124 ++++++++++++++++++++++++++------- src/locales/pseudo/messages.po | 124 ++++++++++++++++++++++++++------- src/locales/ru/messages.po | 124 ++++++++++++++++++++++++++------- src/locales/zh/messages.po | 124 ++++++++++++++++++++++++++------- 9 files changed, 882 insertions(+), 234 deletions(-) diff --git a/src/locales/de/messages.po b/src/locales/de/messages.po index 6392c4b82e..5a9cb3f2c0 100644 --- a/src/locales/de/messages.po +++ b/src/locales/de/messages.po @@ -323,6 +323,7 @@ msgstr "Max {0} Short überschritten" msgid "Cancel Stop-Loss Order" msgstr "Stop-Loss Order abbrechen" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool USD exceeded" msgstr "" @@ -670,6 +671,10 @@ msgstr "Ungültiger Slippagewert" msgid "{0} price" msgstr "{0} Preis" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} buyable amount exceeded" +msgstr "" + #: src/components/Glp/GlpSwap.js msgid "Sell submitted!" msgstr "Verkauf übermittelt!" @@ -720,10 +725,12 @@ msgstr "" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Pay" @@ -891,6 +898,10 @@ msgstr "Min. Empfangen" msgid "Estimated Fee Refund" msgstr "Geschätzte Gebührenerstattung" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling Shift request" +msgstr "" + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "" @@ -1192,6 +1203,11 @@ msgstr "Wenn du eine bestehende Position hast, wird die Position für {0} USD ge msgid "Transfer Submitted" msgstr "Transfer übermittelt" +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +msgid "Shift GM" +msgstr "" + #: src/domain/synthetics/sidecarOrders/utils.ts msgid "Price below Liq. Price." msgstr "" @@ -1232,6 +1248,10 @@ msgstr "Swap Order Erstellung fehlgeschlagen." msgid "Chart positions" msgstr "Chart-Positionen" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending Shift request" +msgstr "" + #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "Der Auftrag wird nur ausgeführt, wenn die Preisbedingungen erfüllt sind und genügend Liquidität vorhanden ist." @@ -1275,6 +1295,10 @@ msgstr "Gebühren" msgid "V2 Avalanche Fuji" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order cancelled" +msgstr "" + #: src/components/Synthetics/StatusNotification/OrderStatusNotification.tsx msgid "Order executed" msgstr "" @@ -1526,6 +1550,10 @@ msgstr "" msgid "Max {0} out" msgstr "Max. {0} heraus" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "<0>Shifting GM" +msgstr "" + #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "" @@ -1566,7 +1594,7 @@ msgstr "" msgid "Forfeit profit not checked" msgstr "Verzicht auf Gewinn nicht geprüft" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Buy GM" msgstr "" @@ -1759,7 +1787,8 @@ msgstr "" #: src/components/Migration/Migration.js #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js @@ -1795,8 +1824,8 @@ msgstr "Kein esGMX zu beanspruchen" msgid "<0>{0} is required for collateral.<1>Short amount for {1} with {2} exceeds potential profits liquidity. Reduce the \"Short Position\" size, or change the \"Collateral In\" token." msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Acknowledge high Price Impact" msgstr "" @@ -1947,6 +1976,10 @@ msgstr "Dein Wallet: {0}" msgid "You have an active order to decrease {longOrShortText} {sizeInToken} {0} (${1}) at {prefix} {2}" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} sellable amount exceeded" +msgstr "" + #: src/components/Glp/GlpSwap.js #: src/pages/Dashboard/DashboardV2.tsx msgid "TOKEN" @@ -2047,6 +2080,10 @@ msgstr "Reduziere die Anzahl der Wallet-Popups mit One-Click Trading. Diese Opti msgid "The maximum number of authorized Actions has been reached. Re-authorize a higher value using the \"<0>Max allowed actions\" field." msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order executed" +msgstr "" + #: src/pages/Stake/StakeV2.tsx msgid "You need a total of at least {0} {stakeTokenLabel} to vest {1} esGMX." msgstr "" @@ -2085,6 +2122,10 @@ msgstr "Der Mark-Preis hat sich geändert. Erhöhe dein zulässiges Slippage, in msgid "TVL" msgstr "TVL" +#: src/components/Synthetics/GmSwap/GmFees/GmFees.tsx +msgid "Shift Fee" +msgstr "" + #: src/pages/Dashboard/DashboardV2.tsx #: src/pages/Dashboard/DashboardV2.tsx msgid "GLP Pool" @@ -2788,7 +2829,7 @@ msgstr "" msgid "Positions{0}" msgstr "" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Unknown Error" msgstr "" @@ -3187,6 +3228,10 @@ msgstr "Ansicht im Explorer" msgid "Bonus APR" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift request sent" +msgstr "" + #: src/pages/Jobs/Jobs.js msgid "GMX is not actively looking for new hires at the moment. However, if you think you can contribute to the project, please email <0>jobs@gmx.io." msgstr "GMX sucht im Moment nicht aktiv nach neuen Mitarbeitern. Wenn du jedoch der Meinung bist, dass du zu dem Projekt beitragen kannst, sende bitte eine E-Mail an <0>jobs@gmx.io." @@ -3211,6 +3256,11 @@ msgstr "" msgid "Unsupported network" msgstr "" +#: src/components/Synthetics/GmList/GmList.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shift" +msgstr "" + #: src/components/Exchange/PositionsList.js #: src/components/Synthetics/PositionItem/PositionItem.tsx #: src/components/Synthetics/PositionItem/PositionItem.tsx @@ -3729,8 +3779,8 @@ msgstr "" msgid "UI Fee" msgstr "" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Execute order simulation failed." msgstr "" @@ -4381,6 +4431,7 @@ msgstr "Swap Order übermittelt!" #: src/components/Synthetics/PositionList/PositionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Loading..." msgstr "Lädt..." @@ -5141,6 +5192,7 @@ msgstr "Max {0} Long überschritten" #: src/components/Exchange/PositionEditor.js #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Amount should be greater than zero" msgstr "" @@ -5157,9 +5209,11 @@ msgstr "{formattedNetRate} / 1h" #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Balance" @@ -5460,6 +5514,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts #: src/pages/ClaimEsGmx/ClaimEsGmx.js #: src/pages/Stake/StakeV1.js #: src/pages/Stake/StakeV1.js @@ -5509,6 +5564,10 @@ msgstr "Scanne den QR-Code" msgid "Claims ({totalClaimables})" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Unknown shift GM order" +msgstr "" + #: src/components/Exchange/TradeHistory.js msgid "Max leverage of 100x was exceeded, the remaining collateral after deducting losses and fees have been sent back to your account:" msgstr "Die max. Hebelwirkung von 100x wurde überschritten, das verbleibende Kollateral wird nach Abzug von Verlusten und Gebühren wurde auf dein Konto zurücküberwiesen:" @@ -5830,10 +5889,12 @@ msgstr "" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/PositionEditor/PositionEditor.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx @@ -5953,6 +6014,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Insufficient {0} balance" msgstr "Unzureichende {0} Balance" @@ -6031,6 +6093,7 @@ msgstr "Datum" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Price Impact not yet acknowledged" msgstr "" @@ -6295,8 +6358,8 @@ msgstr "Swap{0} übermittelt!" msgid "Referral Code does not exist" msgstr "Referral Code existiert nicht" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/showMarketToast.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/useUpdateByQueryParams.tsx msgid "<0>GM: <1>{indexName}<2>[{poolName}] <3>selected in order form" msgstr "" @@ -6346,7 +6409,7 @@ msgstr "" msgid "GMX community discussion" msgstr "GMX Community-Diskussion" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Sell GM" msgstr "" @@ -6361,6 +6424,10 @@ msgstr "" msgid "Fulfilling Sell request" msgstr "" +#: src/domain/synthetics/markets/createShiftTxn.ts +msgid "Shift error." +msgstr "" + #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js @@ -6417,9 +6484,9 @@ msgstr "" msgid "Long" msgstr "Long" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx #: src/components/Synthetics/MarketsList/NetFeeTooltip.tsx #: src/components/Synthetics/PoolSelector2/PoolSelector2.tsx #: src/components/Synthetics/TradeBox/MarketPoolSelectorRow.tsx @@ -6656,7 +6723,6 @@ msgstr "Ordergröße ist größer als die Position. Wird nur ausgeführt, wenn s #: src/components/Header/AppHeaderLinks.tsx #: src/components/Synthetics/GmList/GmList.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx msgid "Buy" msgstr "Kaufen" @@ -6702,6 +6768,10 @@ msgstr "" msgid "Referral Terms" msgstr "Referralbedingungen" +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shifting GM..." +msgstr "" + #: src/components/Synthetics/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Synthetics/MarketTokenSelector/MarketTokenSelector.tsx msgid "No markets matched." @@ -6719,6 +6789,7 @@ msgstr "Ziel Min. Betrag" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed Pay amount" msgstr "" @@ -7208,6 +7279,7 @@ msgstr "" msgid "Enter a new size or price" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool amount exceeded" msgstr "" @@ -7508,7 +7580,7 @@ msgstr "" msgid "Price Impact Rebate Claimed" msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Consider selecting and using the \"Pair\" option to reduce the Price Impact." msgstr "" diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index 0ed45a6977..7a5bd9dba1 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -323,6 +323,7 @@ msgstr "Max {0} short exceeded" msgid "Cancel Stop-Loss Order" msgstr "Cancel Stop-Loss Order" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool USD exceeded" msgstr "Max pool USD exceeded" @@ -670,6 +671,10 @@ msgstr "Invalid slippage value" msgid "{0} price" msgstr "{0} price" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} buyable amount exceeded" +msgstr "Max {0} buyable amount exceeded" + #: src/components/Glp/GlpSwap.js msgid "Sell submitted!" msgstr "Sell submitted!" @@ -720,10 +725,12 @@ msgstr "Liq. {0} {longOrShortText}" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Pay" @@ -891,6 +898,10 @@ msgstr "Min. Receive" msgid "Estimated Fee Refund" msgstr "Estimated Fee Refund" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling Shift request" +msgstr "Fulfilling Shift request" + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "Deactivating..." @@ -1192,6 +1203,11 @@ msgstr "If you have an existing position, the position will be closed at {0} USD msgid "Transfer Submitted" msgstr "Transfer Submitted" +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +msgid "Shift GM" +msgstr "Shift GM" + #: src/domain/synthetics/sidecarOrders/utils.ts msgid "Price below Liq. Price." msgstr "Price below Liq. Price." @@ -1232,6 +1248,10 @@ msgstr "Swap Order creation failed." msgid "Chart positions" msgstr "Chart positions" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending Shift request" +msgstr "Sending Shift request" + #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "The order will only execute if the price conditions are met and there is sufficient liquidity" @@ -1275,6 +1295,10 @@ msgstr "fee" msgid "V2 Avalanche Fuji" msgstr "V2 Avalanche Fuji" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order cancelled" +msgstr "Shift order cancelled" + #: src/components/Synthetics/StatusNotification/OrderStatusNotification.tsx msgid "Order executed" msgstr "Order executed" @@ -1526,6 +1550,10 @@ msgstr "Pair" msgid "Max {0} out" msgstr "Max {0} out" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "<0>Shifting GM" +msgstr "<0>Shifting GM" + #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "Updating Subaccount" @@ -1566,7 +1594,7 @@ msgstr "Accrued Negative Funding Fee" msgid "Forfeit profit not checked" msgstr "Forfeit profit not checked" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Buy GM" msgstr "Buy GM" @@ -1759,7 +1787,8 @@ msgstr "We value your experience and insights and invite you to participate in a #: src/components/Migration/Migration.js #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js @@ -1795,8 +1824,8 @@ msgstr "No esGMX to claim" msgid "<0>{0} is required for collateral.<1>Short amount for {1} with {2} exceeds potential profits liquidity. Reduce the \"Short Position\" size, or change the \"Collateral In\" token." msgstr "<0>{0} is required for collateral.<1>Short amount for {1} with {2} exceeds potential profits liquidity. Reduce the \"Short Position\" size, or change the \"Collateral In\" token." -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Acknowledge high Price Impact" msgstr "Acknowledge high Price Impact" @@ -1947,6 +1976,10 @@ msgstr "Your wallet: {0}" msgid "You have an active order to decrease {longOrShortText} {sizeInToken} {0} (${1}) at {prefix} {2}" msgstr "You have an active order to decrease {longOrShortText} {sizeInToken} {0} (${1}) at {prefix} {2}" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} sellable amount exceeded" +msgstr "Max {0} sellable amount exceeded" + #: src/components/Glp/GlpSwap.js #: src/pages/Dashboard/DashboardV2.tsx msgid "TOKEN" @@ -2047,6 +2080,10 @@ msgstr "Reduce wallet signing popups with One-Click Trading. This option is also msgid "The maximum number of authorized Actions has been reached. Re-authorize a higher value using the \"<0>Max allowed actions\" field." msgstr "The maximum number of authorized Actions has been reached. Re-authorize a higher value using the \"<0>Max allowed actions\" field." +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order executed" +msgstr "Shift order executed" + #: src/pages/Stake/StakeV2.tsx msgid "You need a total of at least {0} {stakeTokenLabel} to vest {1} esGMX." msgstr "You need a total of at least {0} {stakeTokenLabel} to vest {1} esGMX." @@ -2085,6 +2122,10 @@ msgstr "The mark price has changed, consider increasing your Allowed Slippage by msgid "TVL" msgstr "TVL" +#: src/components/Synthetics/GmSwap/GmFees/GmFees.tsx +msgid "Shift Fee" +msgstr "Shift Fee" + #: src/pages/Dashboard/DashboardV2.tsx #: src/pages/Dashboard/DashboardV2.tsx msgid "GLP Pool" @@ -2788,7 +2829,7 @@ msgstr "Accrued Price Impact Rebates. They will become Claimable after some time msgid "Positions{0}" msgstr "Positions{0}" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Unknown Error" msgstr "Unknown Error" @@ -3187,6 +3228,10 @@ msgstr "View in Explorer" msgid "Bonus APR" msgstr "Bonus APR" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift request sent" +msgstr "Shift request sent" + #: src/pages/Jobs/Jobs.js msgid "GMX is not actively looking for new hires at the moment. However, if you think you can contribute to the project, please email <0>jobs@gmx.io." msgstr "GMX is not actively looking for new hires at the moment. However, if you think you can contribute to the project, please email <0>jobs@gmx.io." @@ -3211,6 +3256,11 @@ msgstr "You can transfer AVAX from other networks to Avalanche using any of the msgid "Unsupported network" msgstr "Unsupported network" +#: src/components/Synthetics/GmList/GmList.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shift" +msgstr "Shift" + #: src/components/Exchange/PositionsList.js #: src/components/Synthetics/PositionItem/PositionItem.tsx #: src/components/Synthetics/PositionItem/PositionItem.tsx @@ -3732,8 +3782,8 @@ msgstr "Select a token" msgid "UI Fee" msgstr "UI Fee" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Execute order simulation failed." msgstr "Execute order simulation failed." @@ -4384,6 +4434,7 @@ msgstr "Swap Order submitted!" #: src/components/Synthetics/PositionList/PositionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Loading..." msgstr "Loading..." @@ -5144,6 +5195,7 @@ msgstr "Max {0} long exceeded" #: src/components/Exchange/PositionEditor.js #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Amount should be greater than zero" msgstr "Amount should be greater than zero" @@ -5160,9 +5212,11 @@ msgstr "{formattedNetRate} / 1h" #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Balance" @@ -5463,6 +5517,7 @@ msgstr "Max Out" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts #: src/pages/ClaimEsGmx/ClaimEsGmx.js #: src/pages/Stake/StakeV1.js #: src/pages/Stake/StakeV1.js @@ -5512,6 +5567,10 @@ msgstr "Scan the QR code" msgid "Claims ({totalClaimables})" msgstr "Claims ({totalClaimables})" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Unknown shift GM order" +msgstr "Unknown shift GM order" + #: src/components/Exchange/TradeHistory.js msgid "Max leverage of 100x was exceeded, the remaining collateral after deducting losses and fees have been sent back to your account:" msgstr "Max leverage of 100x was exceeded, the remaining collateral after deducting losses and fees have been sent back to your account:" @@ -5836,10 +5895,12 @@ msgstr "Claim <0>{0}" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/PositionEditor/PositionEditor.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx @@ -5959,6 +6020,7 @@ msgstr "Select a collateral" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Insufficient {0} balance" msgstr "Insufficient {0} balance" @@ -6037,6 +6099,7 @@ msgstr "Date" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Price Impact not yet acknowledged" msgstr "Price Impact not yet acknowledged" @@ -6301,8 +6364,8 @@ msgstr "Swap {0} submitted!" msgid "Referral Code does not exist" msgstr "Referral Code does not exist" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/showMarketToast.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/useUpdateByQueryParams.tsx msgid "<0>GM: <1>{indexName}<2>[{poolName}] <3>selected in order form" msgstr "<0>GM: <1>{indexName}<2>[{poolName}] <3>selected in order form" @@ -6352,7 +6415,7 @@ msgstr "Confirm Claim" msgid "GMX community discussion" msgstr "GMX community discussion" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Sell GM" msgstr "Sell GM" @@ -6367,6 +6430,10 @@ msgstr "View on Arbitrum" msgid "Fulfilling Sell request" msgstr "Fulfilling Sell request" +#: src/domain/synthetics/markets/createShiftTxn.ts +msgid "Shift error." +msgstr "Shift error." + #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js @@ -6423,9 +6490,9 @@ msgstr "Fulfilling Sell request" msgid "Long" msgstr "Long" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx #: src/components/Synthetics/MarketsList/NetFeeTooltip.tsx #: src/components/Synthetics/PoolSelector2/PoolSelector2.tsx #: src/components/Synthetics/TradeBox/MarketPoolSelectorRow.tsx @@ -6662,7 +6729,6 @@ msgstr "Order size is bigger than position, will only be executable if position #: src/components/Header/AppHeaderLinks.tsx #: src/components/Synthetics/GmList/GmList.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx msgid "Buy" msgstr "Buy" @@ -6708,6 +6774,10 @@ msgstr "GLP Vault" msgid "Referral Terms" msgstr "Referral Terms" +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shifting GM..." +msgstr "Shifting GM..." + #: src/components/Synthetics/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Synthetics/MarketTokenSelector/MarketTokenSelector.tsx msgid "No markets matched." @@ -6725,6 +6795,7 @@ msgstr "Target Min Amount" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed Pay amount" msgstr "Fees exceed Pay amount" @@ -7214,6 +7285,7 @@ msgstr "Deposit Fee" msgid "Enter a new size or price" msgstr "Enter a new size or price" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool amount exceeded" msgstr "Max pool amount exceeded" @@ -7514,7 +7586,7 @@ msgstr "Ecosystem Projects" msgid "Price Impact Rebate Claimed" msgstr "Price Impact Rebate Claimed" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Consider selecting and using the \"Pair\" option to reduce the Price Impact." msgstr "Consider selecting and using the \"Pair\" option to reduce the Price Impact." diff --git a/src/locales/es/messages.po b/src/locales/es/messages.po index 622112767f..d9ce7f5b47 100644 --- a/src/locales/es/messages.po +++ b/src/locales/es/messages.po @@ -323,6 +323,7 @@ msgstr "Superados cortos Máx. de {0}" msgid "Cancel Stop-Loss Order" msgstr "Cancelar Orden de Stop-Loss" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool USD exceeded" msgstr "" @@ -670,6 +671,10 @@ msgstr "Valor de deslizamiento no válido" msgid "{0} price" msgstr "{0} precio" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} buyable amount exceeded" +msgstr "" + #: src/components/Glp/GlpSwap.js msgid "Sell submitted!" msgstr "¡Venta enviada!" @@ -720,10 +725,12 @@ msgstr "" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Pay" @@ -891,6 +898,10 @@ msgstr "Min. a Recibir" msgid "Estimated Fee Refund" msgstr "Reembolso de Comisión Estimado" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling Shift request" +msgstr "" + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "" @@ -1192,6 +1203,11 @@ msgstr "Si tienes una posición existente, la posición se cerrará a {0} USD.<0 msgid "Transfer Submitted" msgstr "Transferencia Enviada" +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +msgid "Shift GM" +msgstr "" + #: src/domain/synthetics/sidecarOrders/utils.ts msgid "Price below Liq. Price." msgstr "" @@ -1232,6 +1248,10 @@ msgstr "Falló la creación de la Orden de Intercambio." msgid "Chart positions" msgstr "Posiciones en gráfca" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending Shift request" +msgstr "" + #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "La orden sólo se ejecutará si las condiciones de precio se alcanzan y hay suficiente liquidez" @@ -1275,6 +1295,10 @@ msgstr "comisión" msgid "V2 Avalanche Fuji" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order cancelled" +msgstr "" + #: src/components/Synthetics/StatusNotification/OrderStatusNotification.tsx msgid "Order executed" msgstr "" @@ -1526,6 +1550,10 @@ msgstr "" msgid "Max {0} out" msgstr "Máx. {0} fuera" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "<0>Shifting GM" +msgstr "" + #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "" @@ -1566,7 +1594,7 @@ msgstr "" msgid "Forfeit profit not checked" msgstr "Renuncia de ganancias no marcada" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Buy GM" msgstr "" @@ -1759,7 +1787,8 @@ msgstr "" #: src/components/Migration/Migration.js #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js @@ -1795,8 +1824,8 @@ msgstr "No hay esGMX para reclamar" msgid "<0>{0} is required for collateral.<1>Short amount for {1} with {2} exceeds potential profits liquidity. Reduce the \"Short Position\" size, or change the \"Collateral In\" token." msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Acknowledge high Price Impact" msgstr "" @@ -1947,6 +1976,10 @@ msgstr "Tu monedero: {0}" msgid "You have an active order to decrease {longOrShortText} {sizeInToken} {0} (${1}) at {prefix} {2}" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} sellable amount exceeded" +msgstr "" + #: src/components/Glp/GlpSwap.js #: src/pages/Dashboard/DashboardV2.tsx msgid "TOKEN" @@ -2047,6 +2080,10 @@ msgstr "Reduce las ventanas emergentes de firma de billetera con el trading de u msgid "The maximum number of authorized Actions has been reached. Re-authorize a higher value using the \"<0>Max allowed actions\" field." msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order executed" +msgstr "" + #: src/pages/Stake/StakeV2.tsx msgid "You need a total of at least {0} {stakeTokenLabel} to vest {1} esGMX." msgstr "" @@ -2085,6 +2122,10 @@ msgstr "El precio de referencia ha cambiado, considera incrementar el deslizamie msgid "TVL" msgstr "TVL" +#: src/components/Synthetics/GmSwap/GmFees/GmFees.tsx +msgid "Shift Fee" +msgstr "" + #: src/pages/Dashboard/DashboardV2.tsx #: src/pages/Dashboard/DashboardV2.tsx msgid "GLP Pool" @@ -2788,7 +2829,7 @@ msgstr "" msgid "Positions{0}" msgstr "" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Unknown Error" msgstr "" @@ -3187,6 +3228,10 @@ msgstr "Ver en el Explorador" msgid "Bonus APR" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift request sent" +msgstr "" + #: src/pages/Jobs/Jobs.js msgid "GMX is not actively looking for new hires at the moment. However, if you think you can contribute to the project, please email <0>jobs@gmx.io." msgstr "GMX no se encuentra contratando en este momento. Sin embargo, si crees que puedes contribuir al proyecto, manda un email por favor <0>jobs@gmx.io." @@ -3211,6 +3256,11 @@ msgstr "" msgid "Unsupported network" msgstr "" +#: src/components/Synthetics/GmList/GmList.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shift" +msgstr "" + #: src/components/Exchange/PositionsList.js #: src/components/Synthetics/PositionItem/PositionItem.tsx #: src/components/Synthetics/PositionItem/PositionItem.tsx @@ -3729,8 +3779,8 @@ msgstr "" msgid "UI Fee" msgstr "" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Execute order simulation failed." msgstr "" @@ -4381,6 +4431,7 @@ msgstr "¡Orden de Intercambio enviada!" #: src/components/Synthetics/PositionList/PositionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Loading..." msgstr "Cargando..." @@ -5141,6 +5192,7 @@ msgstr "Superados largos Máx. de {0}" #: src/components/Exchange/PositionEditor.js #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Amount should be greater than zero" msgstr "" @@ -5157,9 +5209,11 @@ msgstr "{formattedNetRate} / 1h" #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Balance" @@ -5460,6 +5514,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts #: src/pages/ClaimEsGmx/ClaimEsGmx.js #: src/pages/Stake/StakeV1.js #: src/pages/Stake/StakeV1.js @@ -5509,6 +5564,10 @@ msgstr "Escanear el código QR" msgid "Claims ({totalClaimables})" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Unknown shift GM order" +msgstr "" + #: src/components/Exchange/TradeHistory.js msgid "Max leverage of 100x was exceeded, the remaining collateral after deducting losses and fees have been sent back to your account:" msgstr "El apalancamiento máximo de 100x ha sido superado, la garantía restante después de restar las pérdidas y comisiones ha sido enviada de vuelta a tu cuenta:" @@ -5830,10 +5889,12 @@ msgstr "" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/PositionEditor/PositionEditor.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx @@ -5953,6 +6014,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Insufficient {0} balance" msgstr "Balance {0} insuficiente" @@ -6031,6 +6093,7 @@ msgstr "Fecha" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Price Impact not yet acknowledged" msgstr "" @@ -6295,8 +6358,8 @@ msgstr "¡Intercambio {0} enviado!" msgid "Referral Code does not exist" msgstr "El Código de Referido no existe" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/showMarketToast.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/useUpdateByQueryParams.tsx msgid "<0>GM: <1>{indexName}<2>[{poolName}] <3>selected in order form" msgstr "" @@ -6346,7 +6409,7 @@ msgstr "" msgid "GMX community discussion" msgstr "Debate comunitario de GMX" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Sell GM" msgstr "" @@ -6361,6 +6424,10 @@ msgstr "" msgid "Fulfilling Sell request" msgstr "" +#: src/domain/synthetics/markets/createShiftTxn.ts +msgid "Shift error." +msgstr "" + #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js @@ -6417,9 +6484,9 @@ msgstr "" msgid "Long" msgstr "Largo" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx #: src/components/Synthetics/MarketsList/NetFeeTooltip.tsx #: src/components/Synthetics/PoolSelector2/PoolSelector2.tsx #: src/components/Synthetics/TradeBox/MarketPoolSelectorRow.tsx @@ -6656,7 +6723,6 @@ msgstr "El tamaño de la orden es mayor que la posición, sólo se ejecutará si #: src/components/Header/AppHeaderLinks.tsx #: src/components/Synthetics/GmList/GmList.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx msgid "Buy" msgstr "Comprar" @@ -6702,6 +6768,10 @@ msgstr "" msgid "Referral Terms" msgstr "Términos de Referido" +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shifting GM..." +msgstr "" + #: src/components/Synthetics/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Synthetics/MarketTokenSelector/MarketTokenSelector.tsx msgid "No markets matched." @@ -6719,6 +6789,7 @@ msgstr "Cantidad Mínima Objetivo" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed Pay amount" msgstr "" @@ -7208,6 +7279,7 @@ msgstr "" msgid "Enter a new size or price" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool amount exceeded" msgstr "" @@ -7508,7 +7580,7 @@ msgstr "" msgid "Price Impact Rebate Claimed" msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Consider selecting and using the \"Pair\" option to reduce the Price Impact." msgstr "" diff --git a/src/locales/fr/messages.po b/src/locales/fr/messages.po index 52f1107bc6..a1b5b9c401 100644 --- a/src/locales/fr/messages.po +++ b/src/locales/fr/messages.po @@ -323,6 +323,7 @@ msgstr "Max {0} short dépassé" msgid "Cancel Stop-Loss Order" msgstr "Annuler l'ordre stop-loss" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool USD exceeded" msgstr "" @@ -670,6 +671,10 @@ msgstr "Valeur de glissement invalide" msgid "{0} price" msgstr "{0} prix" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} buyable amount exceeded" +msgstr "" + #: src/components/Glp/GlpSwap.js msgid "Sell submitted!" msgstr "Vente soumise !" @@ -720,10 +725,12 @@ msgstr "" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Pay" @@ -891,6 +898,10 @@ msgstr "Reçu Min." msgid "Estimated Fee Refund" msgstr "Remboursement estimé des frais" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling Shift request" +msgstr "" + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "" @@ -1192,6 +1203,11 @@ msgstr "Si vous avez une position existante, la position sera fermée à {0} USD msgid "Transfer Submitted" msgstr "Transfert soumis" +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +msgid "Shift GM" +msgstr "" + #: src/domain/synthetics/sidecarOrders/utils.ts msgid "Price below Liq. Price." msgstr "" @@ -1232,6 +1248,10 @@ msgstr "Ordre d'échange échoué." msgid "Chart positions" msgstr "Positions sur les graphiques" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending Shift request" +msgstr "" + #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "L'ordre ne sera exécuté que si les conditions de prix sont remplies et si la liquidité est suffisante." @@ -1275,6 +1295,10 @@ msgstr "frais" msgid "V2 Avalanche Fuji" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order cancelled" +msgstr "" + #: src/components/Synthetics/StatusNotification/OrderStatusNotification.tsx msgid "Order executed" msgstr "" @@ -1526,6 +1550,10 @@ msgstr "" msgid "Max {0} out" msgstr "Sortir en {0} max" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "<0>Shifting GM" +msgstr "" + #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "" @@ -1566,7 +1594,7 @@ msgstr "" msgid "Forfeit profit not checked" msgstr "Renoncer aux profits non vérifiés" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Buy GM" msgstr "" @@ -1759,7 +1787,8 @@ msgstr "" #: src/components/Migration/Migration.js #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js @@ -1795,8 +1824,8 @@ msgstr "Pas d'esGMX à réclamer" msgid "<0>{0} is required for collateral.<1>Short amount for {1} with {2} exceeds potential profits liquidity. Reduce the \"Short Position\" size, or change the \"Collateral In\" token." msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Acknowledge high Price Impact" msgstr "" @@ -1947,6 +1976,10 @@ msgstr "Votre portefeuille: {0}" msgid "You have an active order to decrease {longOrShortText} {sizeInToken} {0} (${1}) at {prefix} {2}" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} sellable amount exceeded" +msgstr "" + #: src/components/Glp/GlpSwap.js #: src/pages/Dashboard/DashboardV2.tsx msgid "TOKEN" @@ -2047,6 +2080,10 @@ msgstr "Réduisez les popups de signature de portefeuille avec le trading en un msgid "The maximum number of authorized Actions has been reached. Re-authorize a higher value using the \"<0>Max allowed actions\" field." msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order executed" +msgstr "" + #: src/pages/Stake/StakeV2.tsx msgid "You need a total of at least {0} {stakeTokenLabel} to vest {1} esGMX." msgstr "" @@ -2085,6 +2122,10 @@ msgstr "Le prix du marché a changé, envisagez d'augmenter votre marge autoris msgid "TVL" msgstr "TVL" +#: src/components/Synthetics/GmSwap/GmFees/GmFees.tsx +msgid "Shift Fee" +msgstr "" + #: src/pages/Dashboard/DashboardV2.tsx #: src/pages/Dashboard/DashboardV2.tsx msgid "GLP Pool" @@ -2788,7 +2829,7 @@ msgstr "" msgid "Positions{0}" msgstr "" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Unknown Error" msgstr "" @@ -3187,6 +3228,10 @@ msgstr "Voir dans Explorer" msgid "Bonus APR" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift request sent" +msgstr "" + #: src/pages/Jobs/Jobs.js msgid "GMX is not actively looking for new hires at the moment. However, if you think you can contribute to the project, please email <0>jobs@gmx.io." msgstr "GMX ne recherche pas activement de nouveaux recrutements pour le moment. Toutefois, si vous pensez pouvoir contribuer au projet, veuillez envoyer un courriel à l'adresse suivante <0>jobs@gmx.io." @@ -3211,6 +3256,11 @@ msgstr "" msgid "Unsupported network" msgstr "" +#: src/components/Synthetics/GmList/GmList.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shift" +msgstr "" + #: src/components/Exchange/PositionsList.js #: src/components/Synthetics/PositionItem/PositionItem.tsx #: src/components/Synthetics/PositionItem/PositionItem.tsx @@ -3729,8 +3779,8 @@ msgstr "" msgid "UI Fee" msgstr "" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Execute order simulation failed." msgstr "" @@ -4381,6 +4431,7 @@ msgstr "Ordre d'échange soumis !" #: src/components/Synthetics/PositionList/PositionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Loading..." msgstr "Chargement..." @@ -5141,6 +5192,7 @@ msgstr "Long {0} max dépassé" #: src/components/Exchange/PositionEditor.js #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Amount should be greater than zero" msgstr "" @@ -5157,9 +5209,11 @@ msgstr "{formattedNetRate} / 1h" #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Balance" @@ -5460,6 +5514,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts #: src/pages/ClaimEsGmx/ClaimEsGmx.js #: src/pages/Stake/StakeV1.js #: src/pages/Stake/StakeV1.js @@ -5509,6 +5564,10 @@ msgstr "Scannez le code QR" msgid "Claims ({totalClaimables})" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Unknown shift GM order" +msgstr "" + #: src/components/Exchange/TradeHistory.js msgid "Max leverage of 100x was exceeded, the remaining collateral after deducting losses and fees have been sent back to your account:" msgstr "Le levier maximum de 100x a été dépassé, le collatéral restant, après déduction des pertes et des frais, a été renvoyé sur votre compte:" @@ -5830,10 +5889,12 @@ msgstr "" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/PositionEditor/PositionEditor.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx @@ -5953,6 +6014,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Insufficient {0} balance" msgstr "Balance {0} insuffisante" @@ -6031,6 +6093,7 @@ msgstr "Date" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Price Impact not yet acknowledged" msgstr "" @@ -6295,8 +6358,8 @@ msgstr "Échanger {0} soumis!" msgid "Referral Code does not exist" msgstr "Ce code de parrainage n'existe pas" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/showMarketToast.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/useUpdateByQueryParams.tsx msgid "<0>GM: <1>{indexName}<2>[{poolName}] <3>selected in order form" msgstr "" @@ -6346,7 +6409,7 @@ msgstr "" msgid "GMX community discussion" msgstr "Discussion de la communauté GMX" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Sell GM" msgstr "" @@ -6361,6 +6424,10 @@ msgstr "" msgid "Fulfilling Sell request" msgstr "" +#: src/domain/synthetics/markets/createShiftTxn.ts +msgid "Shift error." +msgstr "" + #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js @@ -6417,9 +6484,9 @@ msgstr "" msgid "Long" msgstr "Long" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx #: src/components/Synthetics/MarketsList/NetFeeTooltip.tsx #: src/components/Synthetics/PoolSelector2/PoolSelector2.tsx #: src/components/Synthetics/TradeBox/MarketPoolSelectorRow.tsx @@ -6656,7 +6723,6 @@ msgstr "Montant de l'ordre supérieure à la position, il sera exécutable seule #: src/components/Header/AppHeaderLinks.tsx #: src/components/Synthetics/GmList/GmList.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx msgid "Buy" msgstr "Acheter" @@ -6702,6 +6768,10 @@ msgstr "" msgid "Referral Terms" msgstr "Termes de parrainage" +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shifting GM..." +msgstr "" + #: src/components/Synthetics/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Synthetics/MarketTokenSelector/MarketTokenSelector.tsx msgid "No markets matched." @@ -6719,6 +6789,7 @@ msgstr "Somme cible min" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed Pay amount" msgstr "" @@ -7208,6 +7279,7 @@ msgstr "" msgid "Enter a new size or price" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool amount exceeded" msgstr "" @@ -7508,7 +7580,7 @@ msgstr "" msgid "Price Impact Rebate Claimed" msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Consider selecting and using the \"Pair\" option to reduce the Price Impact." msgstr "" diff --git a/src/locales/ja/messages.po b/src/locales/ja/messages.po index 335f77ae23..b1a4b680b9 100644 --- a/src/locales/ja/messages.po +++ b/src/locales/ja/messages.po @@ -323,6 +323,7 @@ msgstr "{0}の最大ショート値を超過しています" msgid "Cancel Stop-Loss Order" msgstr "ストップロス注文のキャンセル" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool USD exceeded" msgstr "" @@ -670,6 +671,10 @@ msgstr "無効なスリッページ値" msgid "{0} price" msgstr "{0} 価格" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} buyable amount exceeded" +msgstr "" + #: src/components/Glp/GlpSwap.js msgid "Sell submitted!" msgstr "売却申し込み完了!" @@ -720,10 +725,12 @@ msgstr "" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Pay" @@ -891,6 +898,10 @@ msgstr "最低受け取り額" msgid "Estimated Fee Refund" msgstr "見積もり手数料返金" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling Shift request" +msgstr "" + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "" @@ -1192,6 +1203,11 @@ msgstr "もし現在ポジションがある場合、そのポジションは{0} msgid "Transfer Submitted" msgstr "移転申し込み完了" +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +msgid "Shift GM" +msgstr "" + #: src/domain/synthetics/sidecarOrders/utils.ts msgid "Price below Liq. Price." msgstr "" @@ -1232,6 +1248,10 @@ msgstr "スワップ注文が作成できませんでした。" msgid "Chart positions" msgstr "ポジションをチャートに" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending Shift request" +msgstr "" + #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "価格の条件が満たされ、さらに十分な流動性が存在している場合にのみ注文は執行されます。" @@ -1275,6 +1295,10 @@ msgstr "手数料" msgid "V2 Avalanche Fuji" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order cancelled" +msgstr "" + #: src/components/Synthetics/StatusNotification/OrderStatusNotification.tsx msgid "Order executed" msgstr "" @@ -1526,6 +1550,10 @@ msgstr "" msgid "Max {0} out" msgstr "最大 {0} out" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "<0>Shifting GM" +msgstr "" + #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "" @@ -1566,7 +1594,7 @@ msgstr "" msgid "Forfeit profit not checked" msgstr "利益喪失がチェックされていません" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Buy GM" msgstr "" @@ -1759,7 +1787,8 @@ msgstr "" #: src/components/Migration/Migration.js #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js @@ -1795,8 +1824,8 @@ msgstr "請求できるesGMXはありません" msgid "<0>{0} is required for collateral.<1>Short amount for {1} with {2} exceeds potential profits liquidity. Reduce the \"Short Position\" size, or change the \"Collateral In\" token." msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Acknowledge high Price Impact" msgstr "" @@ -1947,6 +1976,10 @@ msgstr "あなたのウォレット: {0}" msgid "You have an active order to decrease {longOrShortText} {sizeInToken} {0} (${1}) at {prefix} {2}" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} sellable amount exceeded" +msgstr "" + #: src/components/Glp/GlpSwap.js #: src/pages/Dashboard/DashboardV2.tsx msgid "TOKEN" @@ -2047,6 +2080,10 @@ msgstr "ワンクリック取引でウォレット署名のポップアップを msgid "The maximum number of authorized Actions has been reached. Re-authorize a higher value using the \"<0>Max allowed actions\" field." msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order executed" +msgstr "" + #: src/pages/Stake/StakeV2.tsx msgid "You need a total of at least {0} {stakeTokenLabel} to vest {1} esGMX." msgstr "" @@ -2085,6 +2122,10 @@ msgstr "マーク価格が変化したため、アドレスの横に表示され msgid "TVL" msgstr "TVL" +#: src/components/Synthetics/GmSwap/GmFees/GmFees.tsx +msgid "Shift Fee" +msgstr "" + #: src/pages/Dashboard/DashboardV2.tsx #: src/pages/Dashboard/DashboardV2.tsx msgid "GLP Pool" @@ -2788,7 +2829,7 @@ msgstr "" msgid "Positions{0}" msgstr "" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Unknown Error" msgstr "" @@ -3187,6 +3228,10 @@ msgstr "Explorerで見る" msgid "Bonus APR" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift request sent" +msgstr "" + #: src/pages/Jobs/Jobs.js msgid "GMX is not actively looking for new hires at the moment. However, if you think you can contribute to the project, please email <0>jobs@gmx.io." msgstr "GMXは現在積極的には求人を行っていませんが、プロジェクトに貢献できると思われる方は、<0>jobs@gmx.ioにメールをしてください。" @@ -3211,6 +3256,11 @@ msgstr "" msgid "Unsupported network" msgstr "" +#: src/components/Synthetics/GmList/GmList.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shift" +msgstr "" + #: src/components/Exchange/PositionsList.js #: src/components/Synthetics/PositionItem/PositionItem.tsx #: src/components/Synthetics/PositionItem/PositionItem.tsx @@ -3729,8 +3779,8 @@ msgstr "" msgid "UI Fee" msgstr "" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Execute order simulation failed." msgstr "" @@ -4381,6 +4431,7 @@ msgstr "スワップ注文提出済!" #: src/components/Synthetics/PositionList/PositionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Loading..." msgstr "ロード中" @@ -5141,6 +5192,7 @@ msgstr "{0}の最大ロング値を超過しています" #: src/components/Exchange/PositionEditor.js #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Amount should be greater than zero" msgstr "" @@ -5157,9 +5209,11 @@ msgstr "{formattedNetRate} / 1 時間" #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Balance" @@ -5460,6 +5514,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts #: src/pages/ClaimEsGmx/ClaimEsGmx.js #: src/pages/Stake/StakeV1.js #: src/pages/Stake/StakeV1.js @@ -5509,6 +5564,10 @@ msgstr "QRコードをスキャン" msgid "Claims ({totalClaimables})" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Unknown shift GM order" +msgstr "" + #: src/components/Exchange/TradeHistory.js msgid "Max leverage of 100x was exceeded, the remaining collateral after deducting losses and fees have been sent back to your account:" msgstr "最大レバレッジ100倍を超過し、損失と手数料を差し引き後に残った担保はアカウントに返送されました:" @@ -5830,10 +5889,12 @@ msgstr "" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/PositionEditor/PositionEditor.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx @@ -5953,6 +6014,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Insufficient {0} balance" msgstr "{0} 残高の不足" @@ -6031,6 +6093,7 @@ msgstr "日付" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Price Impact not yet acknowledged" msgstr "" @@ -6295,8 +6358,8 @@ msgstr "{0}のスワップの申し込み完了!" msgid "Referral Code does not exist" msgstr "紹介コードが存在しません" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/showMarketToast.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/useUpdateByQueryParams.tsx msgid "<0>GM: <1>{indexName}<2>[{poolName}] <3>selected in order form" msgstr "" @@ -6346,7 +6409,7 @@ msgstr "" msgid "GMX community discussion" msgstr "GMXのコミュニティ対話" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Sell GM" msgstr "" @@ -6361,6 +6424,10 @@ msgstr "" msgid "Fulfilling Sell request" msgstr "" +#: src/domain/synthetics/markets/createShiftTxn.ts +msgid "Shift error." +msgstr "" + #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js @@ -6417,9 +6484,9 @@ msgstr "" msgid "Long" msgstr "ロング" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx #: src/components/Synthetics/MarketsList/NetFeeTooltip.tsx #: src/components/Synthetics/PoolSelector2/PoolSelector2.tsx #: src/components/Synthetics/TradeBox/MarketPoolSelectorRow.tsx @@ -6656,7 +6723,6 @@ msgstr "注文サイズがポジションより大きいため、ポジション #: src/components/Header/AppHeaderLinks.tsx #: src/components/Synthetics/GmList/GmList.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx msgid "Buy" msgstr "購入" @@ -6702,6 +6768,10 @@ msgstr "" msgid "Referral Terms" msgstr "紹介の規約" +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shifting GM..." +msgstr "" + #: src/components/Synthetics/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Synthetics/MarketTokenSelector/MarketTokenSelector.tsx msgid "No markets matched." @@ -6719,6 +6789,7 @@ msgstr "最低ターゲット額" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed Pay amount" msgstr "" @@ -7208,6 +7279,7 @@ msgstr "" msgid "Enter a new size or price" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool amount exceeded" msgstr "" @@ -7508,7 +7580,7 @@ msgstr "" msgid "Price Impact Rebate Claimed" msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Consider selecting and using the \"Pair\" option to reduce the Price Impact." msgstr "" diff --git a/src/locales/ko/messages.po b/src/locales/ko/messages.po index 4b6efa82cd..d9eb0cdb7d 100644 --- a/src/locales/ko/messages.po +++ b/src/locales/ko/messages.po @@ -323,6 +323,7 @@ msgstr "{0} 숏 최대치를 초과했습니다." msgid "Cancel Stop-Loss Order" msgstr "손절 주문 취소" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool USD exceeded" msgstr "" @@ -670,6 +671,10 @@ msgstr "유효하지 않은 슬리피지 값" msgid "{0} price" msgstr "{0} 가격" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} buyable amount exceeded" +msgstr "" + #: src/components/Glp/GlpSwap.js msgid "Sell submitted!" msgstr "판매 제출 완료!" @@ -720,10 +725,12 @@ msgstr "" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Pay" @@ -891,6 +898,10 @@ msgstr "최소 수취량" msgid "Estimated Fee Refund" msgstr "예상 수수료 환불" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling Shift request" +msgstr "" + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "" @@ -1192,6 +1203,11 @@ msgstr "포지션이 이미 존재한다면, 해당 포지션은 {0} USD에 종 msgid "Transfer Submitted" msgstr "전송 제출 완료" +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +msgid "Shift GM" +msgstr "" + #: src/domain/synthetics/sidecarOrders/utils.ts msgid "Price below Liq. Price." msgstr "" @@ -1232,6 +1248,10 @@ msgstr "스왑 주문 생성 실패." msgid "Chart positions" msgstr "차트 포지션" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending Shift request" +msgstr "" + #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "주문은 가격 조건 및 유동성 충분 요건이 만족하는 경우에만 실행됩니다" @@ -1275,6 +1295,10 @@ msgstr "수수료" msgid "V2 Avalanche Fuji" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order cancelled" +msgstr "" + #: src/components/Synthetics/StatusNotification/OrderStatusNotification.tsx msgid "Order executed" msgstr "" @@ -1526,6 +1550,10 @@ msgstr "" msgid "Max {0} out" msgstr "최대 {0} out" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "<0>Shifting GM" +msgstr "" + #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "" @@ -1566,7 +1594,7 @@ msgstr "" msgid "Forfeit profit not checked" msgstr "상실 이익이 확인되지 않았습니다." -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Buy GM" msgstr "" @@ -1759,7 +1787,8 @@ msgstr "" #: src/components/Migration/Migration.js #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js @@ -1795,8 +1824,8 @@ msgstr "수령할 수 있는 esGMX가 없습니다" msgid "<0>{0} is required for collateral.<1>Short amount for {1} with {2} exceeds potential profits liquidity. Reduce the \"Short Position\" size, or change the \"Collateral In\" token." msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Acknowledge high Price Impact" msgstr "" @@ -1947,6 +1976,10 @@ msgstr "귀하의 지갑: {0}" msgid "You have an active order to decrease {longOrShortText} {sizeInToken} {0} (${1}) at {prefix} {2}" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} sellable amount exceeded" +msgstr "" + #: src/components/Glp/GlpSwap.js #: src/pages/Dashboard/DashboardV2.tsx msgid "TOKEN" @@ -2047,6 +2080,10 @@ msgstr "원클릭 트레이딩으로 지갑 서명 팝업을 줄이세요. 이 msgid "The maximum number of authorized Actions has been reached. Re-authorize a higher value using the \"<0>Max allowed actions\" field." msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order executed" +msgstr "" + #: src/pages/Stake/StakeV2.tsx msgid "You need a total of at least {0} {stakeTokenLabel} to vest {1} esGMX." msgstr "" @@ -2085,6 +2122,10 @@ msgstr "시장 평균가가 변화되었습니다. 귀하의 주소 옆에 \"... msgid "TVL" msgstr "TVL" +#: src/components/Synthetics/GmSwap/GmFees/GmFees.tsx +msgid "Shift Fee" +msgstr "" + #: src/pages/Dashboard/DashboardV2.tsx #: src/pages/Dashboard/DashboardV2.tsx msgid "GLP Pool" @@ -2788,7 +2829,7 @@ msgstr "" msgid "Positions{0}" msgstr "" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Unknown Error" msgstr "" @@ -3187,6 +3228,10 @@ msgstr "Explorer에서 보기" msgid "Bonus APR" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift request sent" +msgstr "" + #: src/pages/Jobs/Jobs.js msgid "GMX is not actively looking for new hires at the moment. However, if you think you can contribute to the project, please email <0>jobs@gmx.io." msgstr "GMX는 현재 구인하고 있지 않습니다. 하지만 해당 프로젝트에 기여할 수 있다고 생각하시면 email <0>jobs@gmx.io으로 연락해주세요." @@ -3211,6 +3256,11 @@ msgstr "" msgid "Unsupported network" msgstr "" +#: src/components/Synthetics/GmList/GmList.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shift" +msgstr "" + #: src/components/Exchange/PositionsList.js #: src/components/Synthetics/PositionItem/PositionItem.tsx #: src/components/Synthetics/PositionItem/PositionItem.tsx @@ -3729,8 +3779,8 @@ msgstr "" msgid "UI Fee" msgstr "" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Execute order simulation failed." msgstr "" @@ -4381,6 +4431,7 @@ msgstr "스왑 주문 제출 완료!" #: src/components/Synthetics/PositionList/PositionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Loading..." msgstr "로딩중..." @@ -5141,6 +5192,7 @@ msgstr "{0} 롱 최대치를 초과했습니다" #: src/components/Exchange/PositionEditor.js #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Amount should be greater than zero" msgstr "" @@ -5157,9 +5209,11 @@ msgstr "{formattedNetRate} / 1시간" #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Balance" @@ -5460,6 +5514,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts #: src/pages/ClaimEsGmx/ClaimEsGmx.js #: src/pages/Stake/StakeV1.js #: src/pages/Stake/StakeV1.js @@ -5509,6 +5564,10 @@ msgstr "QR 코드 스캔" msgid "Claims ({totalClaimables})" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Unknown shift GM order" +msgstr "" + #: src/components/Exchange/TradeHistory.js msgid "Max leverage of 100x was exceeded, the remaining collateral after deducting losses and fees have been sent back to your account:" msgstr "최대 레버리지 100x를 초과하였습니다. 손실과 수수료를 제외한 나머지 담보 자산은 귀하의 계정으로 반환되었습니다:" @@ -5830,10 +5889,12 @@ msgstr "" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/PositionEditor/PositionEditor.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx @@ -5953,6 +6014,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Insufficient {0} balance" msgstr "{0} 잔고 부족" @@ -6031,6 +6093,7 @@ msgstr "날짜" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Price Impact not yet acknowledged" msgstr "" @@ -6295,8 +6358,8 @@ msgstr "{0} 스왑 제출완료!" msgid "Referral Code does not exist" msgstr "추천인 코드가 존재하지 않습니다" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/showMarketToast.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/useUpdateByQueryParams.tsx msgid "<0>GM: <1>{indexName}<2>[{poolName}] <3>selected in order form" msgstr "" @@ -6346,7 +6409,7 @@ msgstr "" msgid "GMX community discussion" msgstr "GMX 커뮤니티 토론" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Sell GM" msgstr "" @@ -6361,6 +6424,10 @@ msgstr "" msgid "Fulfilling Sell request" msgstr "" +#: src/domain/synthetics/markets/createShiftTxn.ts +msgid "Shift error." +msgstr "" + #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js @@ -6417,9 +6484,9 @@ msgstr "" msgid "Long" msgstr "롱" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx #: src/components/Synthetics/MarketsList/NetFeeTooltip.tsx #: src/components/Synthetics/PoolSelector2/PoolSelector2.tsx #: src/components/Synthetics/TradeBox/MarketPoolSelectorRow.tsx @@ -6656,7 +6723,6 @@ msgstr "주문의 사이즈가 포지션을 초과했습니다. 포지션을 증 #: src/components/Header/AppHeaderLinks.tsx #: src/components/Synthetics/GmList/GmList.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx msgid "Buy" msgstr "구매" @@ -6702,6 +6768,10 @@ msgstr "" msgid "Referral Terms" msgstr "추천 약관" +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shifting GM..." +msgstr "" + #: src/components/Synthetics/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Synthetics/MarketTokenSelector/MarketTokenSelector.tsx msgid "No markets matched." @@ -6719,6 +6789,7 @@ msgstr "최소 목표 수량" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed Pay amount" msgstr "" @@ -7208,6 +7279,7 @@ msgstr "" msgid "Enter a new size or price" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool amount exceeded" msgstr "" @@ -7508,7 +7580,7 @@ msgstr "" msgid "Price Impact Rebate Claimed" msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Consider selecting and using the \"Pair\" option to reduce the Price Impact." msgstr "" diff --git a/src/locales/pseudo/messages.po b/src/locales/pseudo/messages.po index a433a513e0..0f3c34f7fb 100644 --- a/src/locales/pseudo/messages.po +++ b/src/locales/pseudo/messages.po @@ -323,6 +323,7 @@ msgstr "" msgid "Cancel Stop-Loss Order" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool USD exceeded" msgstr "" @@ -670,6 +671,10 @@ msgstr "" msgid "{0} price" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} buyable amount exceeded" +msgstr "" + #: src/components/Glp/GlpSwap.js msgid "Sell submitted!" msgstr "" @@ -720,10 +725,12 @@ msgstr "" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Pay" @@ -891,6 +898,10 @@ msgstr "" msgid "Estimated Fee Refund" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling Shift request" +msgstr "" + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "" @@ -1192,6 +1203,11 @@ msgstr "" msgid "Transfer Submitted" msgstr "" +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +msgid "Shift GM" +msgstr "" + #: src/domain/synthetics/sidecarOrders/utils.ts msgid "Price below Liq. Price." msgstr "" @@ -1232,6 +1248,10 @@ msgstr "" msgid "Chart positions" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending Shift request" +msgstr "" + #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "" @@ -1275,6 +1295,10 @@ msgstr "" msgid "V2 Avalanche Fuji" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order cancelled" +msgstr "" + #: src/components/Synthetics/StatusNotification/OrderStatusNotification.tsx msgid "Order executed" msgstr "" @@ -1526,6 +1550,10 @@ msgstr "" msgid "Max {0} out" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "<0>Shifting GM" +msgstr "" + #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "" @@ -1566,7 +1594,7 @@ msgstr "" msgid "Forfeit profit not checked" msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Buy GM" msgstr "" @@ -1759,7 +1787,8 @@ msgstr "" #: src/components/Migration/Migration.js #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js @@ -1795,8 +1824,8 @@ msgstr "" msgid "<0>{0} is required for collateral.<1>Short amount for {1} with {2} exceeds potential profits liquidity. Reduce the \"Short Position\" size, or change the \"Collateral In\" token." msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Acknowledge high Price Impact" msgstr "" @@ -1947,6 +1976,10 @@ msgstr "" msgid "You have an active order to decrease {longOrShortText} {sizeInToken} {0} (${1}) at {prefix} {2}" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} sellable amount exceeded" +msgstr "" + #: src/components/Glp/GlpSwap.js #: src/pages/Dashboard/DashboardV2.tsx msgid "TOKEN" @@ -2047,6 +2080,10 @@ msgstr "" msgid "The maximum number of authorized Actions has been reached. Re-authorize a higher value using the \"<0>Max allowed actions\" field." msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order executed" +msgstr "" + #: src/pages/Stake/StakeV2.tsx msgid "You need a total of at least {0} {stakeTokenLabel} to vest {1} esGMX." msgstr "" @@ -2085,6 +2122,10 @@ msgstr "" msgid "TVL" msgstr "" +#: src/components/Synthetics/GmSwap/GmFees/GmFees.tsx +msgid "Shift Fee" +msgstr "" + #: src/pages/Dashboard/DashboardV2.tsx #: src/pages/Dashboard/DashboardV2.tsx msgid "GLP Pool" @@ -2788,7 +2829,7 @@ msgstr "" msgid "Positions{0}" msgstr "" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Unknown Error" msgstr "" @@ -3187,6 +3228,10 @@ msgstr "" msgid "Bonus APR" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift request sent" +msgstr "" + #: src/pages/Jobs/Jobs.js msgid "GMX is not actively looking for new hires at the moment. However, if you think you can contribute to the project, please email <0>jobs@gmx.io." msgstr "" @@ -3211,6 +3256,11 @@ msgstr "" msgid "Unsupported network" msgstr "" +#: src/components/Synthetics/GmList/GmList.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shift" +msgstr "" + #: src/components/Exchange/PositionsList.js #: src/components/Synthetics/PositionItem/PositionItem.tsx #: src/components/Synthetics/PositionItem/PositionItem.tsx @@ -3729,8 +3779,8 @@ msgstr "" msgid "UI Fee" msgstr "" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Execute order simulation failed." msgstr "" @@ -4381,6 +4431,7 @@ msgstr "" #: src/components/Synthetics/PositionList/PositionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Loading..." msgstr "" @@ -5141,6 +5192,7 @@ msgstr "" #: src/components/Exchange/PositionEditor.js #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Amount should be greater than zero" msgstr "" @@ -5157,9 +5209,11 @@ msgstr "" #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Balance" @@ -5460,6 +5514,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts #: src/pages/ClaimEsGmx/ClaimEsGmx.js #: src/pages/Stake/StakeV1.js #: src/pages/Stake/StakeV1.js @@ -5509,6 +5564,10 @@ msgstr "" msgid "Claims ({totalClaimables})" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Unknown shift GM order" +msgstr "" + #: src/components/Exchange/TradeHistory.js msgid "Max leverage of 100x was exceeded, the remaining collateral after deducting losses and fees have been sent back to your account:" msgstr "" @@ -5830,10 +5889,12 @@ msgstr "" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/PositionEditor/PositionEditor.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx @@ -5953,6 +6014,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Insufficient {0} balance" msgstr "" @@ -6031,6 +6093,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Price Impact not yet acknowledged" msgstr "" @@ -6295,8 +6358,8 @@ msgstr "" msgid "Referral Code does not exist" msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/showMarketToast.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/useUpdateByQueryParams.tsx msgid "<0>GM: <1>{indexName}<2>[{poolName}] <3>selected in order form" msgstr "" @@ -6346,7 +6409,7 @@ msgstr "" msgid "GMX community discussion" msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Sell GM" msgstr "" @@ -6361,6 +6424,10 @@ msgstr "" msgid "Fulfilling Sell request" msgstr "" +#: src/domain/synthetics/markets/createShiftTxn.ts +msgid "Shift error." +msgstr "" + #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js @@ -6417,9 +6484,9 @@ msgstr "" msgid "Long" msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx #: src/components/Synthetics/MarketsList/NetFeeTooltip.tsx #: src/components/Synthetics/PoolSelector2/PoolSelector2.tsx #: src/components/Synthetics/TradeBox/MarketPoolSelectorRow.tsx @@ -6656,7 +6723,6 @@ msgstr "" #: src/components/Header/AppHeaderLinks.tsx #: src/components/Synthetics/GmList/GmList.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx msgid "Buy" msgstr "" @@ -6702,6 +6768,10 @@ msgstr "" msgid "Referral Terms" msgstr "" +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shifting GM..." +msgstr "" + #: src/components/Synthetics/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Synthetics/MarketTokenSelector/MarketTokenSelector.tsx msgid "No markets matched." @@ -6719,6 +6789,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed Pay amount" msgstr "" @@ -7208,6 +7279,7 @@ msgstr "" msgid "Enter a new size or price" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool amount exceeded" msgstr "" @@ -7508,7 +7580,7 @@ msgstr "" msgid "Price Impact Rebate Claimed" msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Consider selecting and using the \"Pair\" option to reduce the Price Impact." msgstr "" diff --git a/src/locales/ru/messages.po b/src/locales/ru/messages.po index 9921877aa3..154d70f25c 100644 --- a/src/locales/ru/messages.po +++ b/src/locales/ru/messages.po @@ -323,6 +323,7 @@ msgstr "Максимальное {0} превышение шорта" msgid "Cancel Stop-Loss Order" msgstr "Отмена стоп-лосс ордера" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool USD exceeded" msgstr "" @@ -670,6 +671,10 @@ msgstr "Недопустимое значение скольжения" msgid "{0} price" msgstr "{0} цена" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} buyable amount exceeded" +msgstr "" + #: src/components/Glp/GlpSwap.js msgid "Sell submitted!" msgstr "Продажа подтверждена!" @@ -720,10 +725,12 @@ msgstr "" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Pay" @@ -891,6 +898,10 @@ msgstr "Мин. Приём" msgid "Estimated Fee Refund" msgstr "Прогнозируемый Возврат Сбора" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling Shift request" +msgstr "" + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "" @@ -1192,6 +1203,11 @@ msgstr "Если у вас есть существующая позиция, п msgid "Transfer Submitted" msgstr "Перевод Подтверждён" +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +msgid "Shift GM" +msgstr "" + #: src/domain/synthetics/sidecarOrders/utils.ts msgid "Price below Liq. Price." msgstr "" @@ -1232,6 +1248,10 @@ msgstr "Обменный Ордер не создан" msgid "Chart positions" msgstr "Позиции на графике" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending Shift request" +msgstr "" + #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "" @@ -1275,6 +1295,10 @@ msgstr "комиссия" msgid "V2 Avalanche Fuji" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order cancelled" +msgstr "" + #: src/components/Synthetics/StatusNotification/OrderStatusNotification.tsx msgid "Order executed" msgstr "" @@ -1526,6 +1550,10 @@ msgstr "" msgid "Max {0} out" msgstr "Максимальный {0} выход" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "<0>Shifting GM" +msgstr "" + #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "" @@ -1566,7 +1594,7 @@ msgstr "" msgid "Forfeit profit not checked" msgstr "Неустойка за непроверенную прибыль" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Buy GM" msgstr "Купить GM" @@ -1759,7 +1787,8 @@ msgstr "" #: src/components/Migration/Migration.js #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js @@ -1795,8 +1824,8 @@ msgstr "Нет esGMX для получения компенсации" msgid "<0>{0} is required for collateral.<1>Short amount for {1} with {2} exceeds potential profits liquidity. Reduce the \"Short Position\" size, or change the \"Collateral In\" token." msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Acknowledge high Price Impact" msgstr "" @@ -1947,6 +1976,10 @@ msgstr "Ваш кошелёк: {0}" msgid "You have an active order to decrease {longOrShortText} {sizeInToken} {0} (${1}) at {prefix} {2}" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} sellable amount exceeded" +msgstr "" + #: src/components/Glp/GlpSwap.js #: src/pages/Dashboard/DashboardV2.tsx msgid "TOKEN" @@ -2047,6 +2080,10 @@ msgstr "Снизьте количество всплывающих окон по msgid "The maximum number of authorized Actions has been reached. Re-authorize a higher value using the \"<0>Max allowed actions\" field." msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order executed" +msgstr "" + #: src/pages/Stake/StakeV2.tsx msgid "You need a total of at least {0} {stakeTokenLabel} to vest {1} esGMX." msgstr "" @@ -2085,6 +2122,10 @@ msgstr "Цена отметки изменилась, подумайте об у msgid "TVL" msgstr "TVL" +#: src/components/Synthetics/GmSwap/GmFees/GmFees.tsx +msgid "Shift Fee" +msgstr "" + #: src/pages/Dashboard/DashboardV2.tsx #: src/pages/Dashboard/DashboardV2.tsx msgid "GLP Pool" @@ -2788,7 +2829,7 @@ msgstr "" msgid "Positions{0}" msgstr "" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Unknown Error" msgstr "" @@ -3187,6 +3228,10 @@ msgstr "Просмотреть в Проводнике" msgid "Bonus APR" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift request sent" +msgstr "" + #: src/pages/Jobs/Jobs.js msgid "GMX is not actively looking for new hires at the moment. However, if you think you can contribute to the project, please email <0>jobs@gmx.io." msgstr "В настоящее время GMX не ведет активного поиска новых сотрудников. Однако, если вы считаете, что можете внести свой вклад в проект, пожалуйста, напишите <0>jobs@gmx.io." @@ -3211,6 +3256,11 @@ msgstr "" msgid "Unsupported network" msgstr "" +#: src/components/Synthetics/GmList/GmList.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shift" +msgstr "" + #: src/components/Exchange/PositionsList.js #: src/components/Synthetics/PositionItem/PositionItem.tsx #: src/components/Synthetics/PositionItem/PositionItem.tsx @@ -3729,8 +3779,8 @@ msgstr "" msgid "UI Fee" msgstr "" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Execute order simulation failed." msgstr "" @@ -4381,6 +4431,7 @@ msgstr "Обменный Ордер подан!" #: src/components/Synthetics/PositionList/PositionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Loading..." msgstr "Загрузка..." @@ -5141,6 +5192,7 @@ msgstr "Максимальное {0} превышение лонга" #: src/components/Exchange/PositionEditor.js #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Amount should be greater than zero" msgstr "" @@ -5157,9 +5209,11 @@ msgstr "{formattedNetRate} / 1ч" #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Balance" @@ -5460,6 +5514,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts #: src/pages/ClaimEsGmx/ClaimEsGmx.js #: src/pages/Stake/StakeV1.js #: src/pages/Stake/StakeV1.js @@ -5509,6 +5564,10 @@ msgstr "Отсканируйте QR-код" msgid "Claims ({totalClaimables})" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Unknown shift GM order" +msgstr "" + #: src/components/Exchange/TradeHistory.js msgid "Max leverage of 100x was exceeded, the remaining collateral after deducting losses and fees have been sent back to your account:" msgstr "Максимальное кредитное плечо 100x было превышено, оставшийся залог после вычета убытков и комиссий был отправлен обратно на ваш счет:" @@ -5830,10 +5889,12 @@ msgstr "" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/PositionEditor/PositionEditor.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx @@ -5953,6 +6014,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Insufficient {0} balance" msgstr "Недостаточный {0} баланс" @@ -6031,6 +6093,7 @@ msgstr "Дата" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Price Impact not yet acknowledged" msgstr "" @@ -6295,8 +6358,8 @@ msgstr "Обмен {0} подан!" msgid "Referral Code does not exist" msgstr "Реферальный Код не существует" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/showMarketToast.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/useUpdateByQueryParams.tsx msgid "<0>GM: <1>{indexName}<2>[{poolName}] <3>selected in order form" msgstr "" @@ -6346,7 +6409,7 @@ msgstr "" msgid "GMX community discussion" msgstr "Обсуждения в сообществе GMX" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Sell GM" msgstr "Продать GM" @@ -6361,6 +6424,10 @@ msgstr "" msgid "Fulfilling Sell request" msgstr "" +#: src/domain/synthetics/markets/createShiftTxn.ts +msgid "Shift error." +msgstr "" + #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js @@ -6417,9 +6484,9 @@ msgstr "" msgid "Long" msgstr "Лонг" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx #: src/components/Synthetics/MarketsList/NetFeeTooltip.tsx #: src/components/Synthetics/PoolSelector2/PoolSelector2.tsx #: src/components/Synthetics/TradeBox/MarketPoolSelectorRow.tsx @@ -6656,7 +6723,6 @@ msgstr "Размер ордера больше позиции, будет исп #: src/components/Header/AppHeaderLinks.tsx #: src/components/Synthetics/GmList/GmList.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx msgid "Buy" msgstr "Купить" @@ -6702,6 +6768,10 @@ msgstr "" msgid "Referral Terms" msgstr "Реферальные Условия" +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shifting GM..." +msgstr "" + #: src/components/Synthetics/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Synthetics/MarketTokenSelector/MarketTokenSelector.tsx msgid "No markets matched." @@ -6719,6 +6789,7 @@ msgstr "Целевой показатель Мин Суммы" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed Pay amount" msgstr "" @@ -7208,6 +7279,7 @@ msgstr "" msgid "Enter a new size or price" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool amount exceeded" msgstr "" @@ -7508,7 +7580,7 @@ msgstr "" msgid "Price Impact Rebate Claimed" msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Consider selecting and using the \"Pair\" option to reduce the Price Impact." msgstr "" diff --git a/src/locales/zh/messages.po b/src/locales/zh/messages.po index 445d8037ee..98e384d6aa 100644 --- a/src/locales/zh/messages.po +++ b/src/locales/zh/messages.po @@ -323,6 +323,7 @@ msgstr "超过了最大{0}做空范围" msgid "Cancel Stop-Loss Order" msgstr "取消止损订单" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool USD exceeded" msgstr "" @@ -670,6 +671,10 @@ msgstr "无效的滑点值" msgid "{0} price" msgstr "{0}价格" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} buyable amount exceeded" +msgstr "" + #: src/components/Glp/GlpSwap.js msgid "Sell submitted!" msgstr "出售提交" @@ -720,10 +725,12 @@ msgstr "" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Pay" @@ -891,6 +898,10 @@ msgstr "最低接收" msgid "Estimated Fee Refund" msgstr "估计的费用退款" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling Shift request" +msgstr "" + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "" @@ -1192,6 +1203,11 @@ msgstr "如果你有一个现有的头寸,它将在{0}美元被关闭。<0/><1 msgid "Transfer Submitted" msgstr "送出转移" +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +msgid "Shift GM" +msgstr "" + #: src/domain/synthetics/sidecarOrders/utils.ts msgid "Price below Liq. Price." msgstr "" @@ -1232,6 +1248,10 @@ msgstr "创建交易订单失败" msgid "Chart positions" msgstr "图表位置" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending Shift request" +msgstr "" + #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "该订单只在满足价格条件并有足够的流动性时才会执行" @@ -1275,6 +1295,10 @@ msgstr "费用" msgid "V2 Avalanche Fuji" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order cancelled" +msgstr "" + #: src/components/Synthetics/StatusNotification/OrderStatusNotification.tsx msgid "Order executed" msgstr "" @@ -1526,6 +1550,10 @@ msgstr "" msgid "Max {0} out" msgstr "最高{0}以外" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "<0>Shifting GM" +msgstr "" + #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "" @@ -1566,7 +1594,7 @@ msgstr "" msgid "Forfeit profit not checked" msgstr "未检查没收利润" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Buy GM" msgstr "" @@ -1759,7 +1787,8 @@ msgstr "" #: src/components/Migration/Migration.js #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js @@ -1795,8 +1824,8 @@ msgstr "没有esGMX可供索取" msgid "<0>{0} is required for collateral.<1>Short amount for {1} with {2} exceeds potential profits liquidity. Reduce the \"Short Position\" size, or change the \"Collateral In\" token." msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Acknowledge high Price Impact" msgstr "" @@ -1947,6 +1976,10 @@ msgstr "你的钱包: {0}" msgid "You have an active order to decrease {longOrShortText} {sizeInToken} {0} (${1}) at {prefix} {2}" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts +msgid "Max {0} sellable amount exceeded" +msgstr "" + #: src/components/Glp/GlpSwap.js #: src/pages/Dashboard/DashboardV2.tsx msgid "TOKEN" @@ -2047,6 +2080,10 @@ msgstr "通过一键交易减少钱包签名弹出窗口。此选项也可通过 msgid "The maximum number of authorized Actions has been reached. Re-authorize a higher value using the \"<0>Max allowed actions\" field." msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift order executed" +msgstr "" + #: src/pages/Stake/StakeV2.tsx msgid "You need a total of at least {0} {stakeTokenLabel} to vest {1} esGMX." msgstr "" @@ -2085,6 +2122,10 @@ msgstr "标价已改变, 请考虑点击你地址旁的 \"...\" 图示来增加 msgid "TVL" msgstr "TVL" +#: src/components/Synthetics/GmSwap/GmFees/GmFees.tsx +msgid "Shift Fee" +msgstr "" + #: src/pages/Dashboard/DashboardV2.tsx #: src/pages/Dashboard/DashboardV2.tsx msgid "GLP Pool" @@ -2788,7 +2829,7 @@ msgstr "" msgid "Positions{0}" msgstr "" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Unknown Error" msgstr "" @@ -3187,6 +3228,10 @@ msgstr "在浏览器中查看" msgid "Bonus APR" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shift request sent" +msgstr "" + #: src/pages/Jobs/Jobs.js msgid "GMX is not actively looking for new hires at the moment. However, if you think you can contribute to the project, please email <0>jobs@gmx.io." msgstr "GMX目前没有积极寻找新员工。但是,如果你认为你能为项目做出贡献,请发电子邮件到<0>jobs@gmx.io" @@ -3211,6 +3256,11 @@ msgstr "" msgid "Unsupported network" msgstr "" +#: src/components/Synthetics/GmList/GmList.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shift" +msgstr "" + #: src/components/Exchange/PositionsList.js #: src/components/Synthetics/PositionItem/PositionItem.tsx #: src/components/Synthetics/PositionItem/PositionItem.tsx @@ -3729,8 +3779,8 @@ msgstr "" msgid "UI Fee" msgstr "" -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx -#: src/domain/synthetics/orders/simulateExecuteOrderTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx +#: src/domain/synthetics/orders/simulateExecuteTxn.tsx msgid "Execute order simulation failed." msgstr "" @@ -4381,6 +4431,7 @@ msgstr "交易订单送出" #: src/components/Synthetics/PositionList/PositionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Loading..." msgstr "加载中..." @@ -5141,6 +5192,7 @@ msgstr "超过了最大{0}做多范围" #: src/components/Exchange/PositionEditor.js #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Amount should be greater than zero" msgstr "" @@ -5157,9 +5209,11 @@ msgstr "{formattedNetRate} / 1 小时" #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js #: src/components/Glp/GlpSwap.js -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx #: src/components/Synthetics/TradeBox/TradeBox.tsx msgid "Balance" @@ -5460,6 +5514,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts #: src/pages/ClaimEsGmx/ClaimEsGmx.js #: src/pages/Stake/StakeV1.js #: src/pages/Stake/StakeV1.js @@ -5509,6 +5564,10 @@ msgstr "扫描二维码" msgid "Claims ({totalClaimables})" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Unknown shift GM order" +msgstr "" + #: src/components/Exchange/TradeHistory.js msgid "Max leverage of 100x was exceeded, the remaining collateral after deducting losses and fees have been sent back to your account:" msgstr "超过100倍的最大杠杆率,扣除损失和费用后的剩余抵押物已发回你的账户:" @@ -5830,10 +5889,12 @@ msgstr "" #: src/components/Glp/GlpSwap.js #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx #: src/components/Synthetics/PositionEditor/PositionEditor.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx #: src/components/Synthetics/PositionSeller/PositionSeller.tsx @@ -5953,6 +6014,7 @@ msgstr "" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Insufficient {0} balance" msgstr " {0}余额不足" @@ -6031,6 +6093,7 @@ msgstr "日期" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Price Impact not yet acknowledged" msgstr "" @@ -6295,8 +6358,8 @@ msgstr "交易{0}送出!" msgid "Referral Code does not exist" msgstr "推荐代码不存在" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/showMarketToast.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/useUpdateByQueryParams.tsx msgid "<0>GM: <1>{indexName}<2>[{poolName}] <3>selected in order form" msgstr "" @@ -6346,7 +6409,7 @@ msgstr "" msgid "GMX community discussion" msgstr "GMX社区讨论" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Sell GM" msgstr "" @@ -6361,6 +6424,10 @@ msgstr "" msgid "Fulfilling Sell request" msgstr "" +#: src/domain/synthetics/markets/createShiftTxn.ts +msgid "Shift error." +msgstr "" + #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js #: src/components/Exchange/ConfirmationBox.js @@ -6417,9 +6484,9 @@ msgstr "" msgid "Long" msgstr "做多" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx #: src/components/Synthetics/MarketsList/NetFeeTooltip.tsx #: src/components/Synthetics/PoolSelector2/PoolSelector2.tsx #: src/components/Synthetics/TradeBox/MarketPoolSelectorRow.tsx @@ -6656,7 +6723,6 @@ msgstr "订单规模大于头寸,只有在头寸增加的情况下才会执行 #: src/components/Header/AppHeaderLinks.tsx #: src/components/Synthetics/GmList/GmList.tsx #: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx -#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx msgid "Buy" msgstr "购买" @@ -6702,6 +6768,10 @@ msgstr "" msgid "Referral Terms" msgstr "推荐条款" +#: src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +msgid "Shifting GM..." +msgstr "" + #: src/components/Synthetics/ChartTokenSelector/ChartTokenSelector.tsx #: src/components/Synthetics/MarketTokenSelector/MarketTokenSelector.tsx msgid "No markets matched." @@ -6719,6 +6789,7 @@ msgstr "目标最低金额" #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts +#: src/domain/synthetics/trade/utils/validation.ts msgid "Fees exceed Pay amount" msgstr "" @@ -7208,6 +7279,7 @@ msgstr "" msgid "Enter a new size or price" msgstr "" +#: src/domain/synthetics/trade/utils/validation.ts #: src/domain/synthetics/trade/utils/validation.ts msgid "Max pool amount exceeded" msgstr "" @@ -7508,7 +7580,7 @@ msgstr "" msgid "Price Impact Rebate Claimed" msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx msgid "Consider selecting and using the \"Pair\" option to reduce the Price Impact." msgstr "" From c5440848cd8a0058f2230b48cf802f4ec7a30126 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 6 Aug 2024 15:05:03 +0000 Subject: [PATCH 30/62] Refactor shifts --- .../GmDepositWithdrawalBox/InfoRows.tsx | 17 +- .../GmSwapBox/GmShiftBox/GmShiftBox.tsx | 189 ++---------------- .../GmSwapBox/GmShiftBox/useShiftAmounts.tsx | 68 +++++++ .../GmSwapBox/GmShiftBox/useShiftFees.tsx | 64 ++++++ .../GmShiftBox/useShiftSubmitState.tsx | 113 +++++++++++ .../GmSwapBox/GmShiftBox/useUpdateTokens.tsx | 44 ++++ src/domain/synthetics/trade/utils/shift.ts | 2 +- 7 files changed, 317 insertions(+), 180 deletions(-) create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAmounts.tsx create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftFees.tsx create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx create mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useUpdateTokens.tsx diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx index 247e0b501a..37b237e754 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/InfoRows.tsx @@ -1,19 +1,22 @@ import { t } from "@lingui/macro"; -import { ExchangeInfo } from "components/Exchange/ExchangeInfo"; -import ExchangeInfoRow from "components/Exchange/ExchangeInfoRow"; -import { PoolSelector } from "components/MarketSelector/PoolSelector"; -import { GmFees } from "components/Synthetics/GmSwap/GmFees/GmFees"; -import { NetworkFeeRow } from "components/Synthetics/NetworkFeeRow/NetworkFeeRow"; +import { values } from "lodash"; + import { selectMarketsInfoData } from "context/SyntheticsStateContext/selectors/globalSelectors"; import { useSelector } from "context/SyntheticsStateContext/utils"; import { ExecutionFee } from "domain/synthetics/fees"; import { TokensData } from "domain/synthetics/tokens"; import { useGmTokensFavorites } from "domain/synthetics/tokens/useGmTokensFavorites"; import { GmSwapFees } from "domain/synthetics/trade"; -import { values } from "lodash"; -import { HighPriceImpactRow } from "../HighPriceImpactRow"; + import { showMarketToast } from "../showMarketToast"; +import { ExchangeInfo } from "components/Exchange/ExchangeInfo"; +import ExchangeInfoRow from "components/Exchange/ExchangeInfoRow"; +import { PoolSelector } from "components/MarketSelector/PoolSelector"; +import { GmFees } from "components/Synthetics/GmSwap/GmFees/GmFees"; +import { NetworkFeeRow } from "components/Synthetics/NetworkFeeRow/NetworkFeeRow"; +import { HighPriceImpactRow } from "../HighPriceImpactRow"; + export function InfoRows({ indexName, marketAddress, diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx index 201dbe7baa..ac984b992b 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx @@ -1,35 +1,21 @@ import { t } from "@lingui/macro"; -import { useConnectModal } from "@rainbow-me/rainbowkit"; -import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react"; +import { Dispatch, SetStateAction, useCallback, useState } from "react"; import { HIGH_PRICE_IMPACT_BPS } from "config/factors"; import { useSettings } from "context/SettingsContext/SettingsContextProvider"; import { useMarketsInfoData, useTokensData, useUiFeeFactor } from "context/SyntheticsStateContext/hooks/globalsHooks"; import { - selectAccount, selectChainId, selectGasLimits, selectGasPrice, } from "context/SyntheticsStateContext/selectors/globalSelectors"; import { useSelector } from "context/SyntheticsStateContext/utils"; -import { useHasOutdatedUi } from "domain/legacy"; -import { - FeeItem, - estimateExecuteShiftGasLimit, - getExecutionFee, - getFeeItem, - getTotalFeeItem, -} from "domain/synthetics/fees"; -import { estimateShiftOraclePriceCount } from "domain/synthetics/fees/utils/estimateOraclePriceCount"; import { MarketInfo, getMarketIndexName } from "domain/synthetics/markets"; import { useMarketTokensData } from "domain/synthetics/markets/useMarketTokensData"; import { useGmTokensFavorites } from "domain/synthetics/tokens/useGmTokensFavorites"; -import { GmSwapFees } from "domain/synthetics/trade/types"; import useSortedPoolsWithIndexToken from "domain/synthetics/trade/useSortedPoolsWithIndexToken"; -import { getShiftAmounts } from "domain/synthetics/trade/utils/shift"; -import { getCommonError, getGmShiftError } from "domain/synthetics/trade/utils/validation"; import { bigMath } from "lib/bigmath"; -import { formatAmountFree, formatTokenAmount, formatUsd, parseValue } from "lib/numbers"; +import { formatAmountFree, formatTokenAmount, formatUsd } from "lib/numbers"; import { getByKey } from "lib/objects"; import { Mode, Operation } from "../types"; import { useUpdateByQueryParams } from "../useUpdateByQueryParams"; @@ -46,6 +32,10 @@ import { GmConfirmationBox } from "../../GmConfirmationBox/GmConfirmationBox"; import { GmFees } from "../../GmFees/GmFees"; import { HighPriceImpactRow } from "../HighPriceImpactRow"; import { Swap } from "../Swap"; +import { useUpdateTokens } from "./useUpdateTokens"; +import { useShiftAmounts } from "./useShiftAmounts"; +import { useShiftFees } from "./useShiftFees"; +import { useShiftSubmitState } from "./useShiftSubmitState"; export function GmShiftBox({ selectedMarketAddress, @@ -66,10 +56,8 @@ export function GmShiftBox({ const [focusedInput, setFocusedInput] = useState<"selectedMarket" | "toMarket" | undefined>(undefined); const [isConfirmationBoxVisible, setIsConfirmationBoxVisible] = useState(false); const [isHighPriceImpactAccepted, setIsHighPriceImpactAccepted] = useState(false); - const { openConnectModal } = useConnectModal(); const chainId = useSelector(selectChainId); - const account = useSelector(selectAccount); const uiFeeFactor = useUiFeeFactor(); const gasLimits = useSelector(selectGasLimits); const gasPrice = useSelector(selectGasPrice); @@ -87,7 +75,6 @@ export function GmShiftBox({ selectedMarketAddress ); const { shouldDisableValidationForTesting } = useSettings(); - const { data: hasOutdatedUi } = useHasOutdatedUi(); const selectedMarketInfo = getByKey(marketsInfoData, selectedMarketAddress); const selectedIndexName = selectedMarketInfo ? getMarketIndexName(selectedMarketInfo) : "..."; @@ -96,156 +83,35 @@ export function GmShiftBox({ const toIndexName = toMarketInfo ? getMarketIndexName(toMarketInfo) : "..."; const toToken = getByKey(depositMarketTokensData, toMarketAddress); - const amounts = useMemo(() => { - if (!selectedMarketInfo || !selectedToken || !toMarketInfo || !toToken) { - return; - } - - let fromTokenAmount = 0n; - try { - fromTokenAmount = parseValue(selectedMarketText, selectedToken.decimals) ?? 0n; - } catch { - // pass - } - - let toTokenAmount = 0n; - try { - toTokenAmount = parseValue(toMarketText, toToken.decimals) ?? 0n; - } catch { - // pass - } - - const amounts = getShiftAmounts({ - fromMarketInfo: selectedMarketInfo, - fromToken: selectedToken, - fromTokenAmount, - toMarketInfo, - toToken: toToken, - toTokenAmount, - strategy: focusedInput === "selectedMarket" ? "byFromToken" : "byToToken", - uiFeeFactor, - }); - - return amounts; - }, [ - focusedInput, + const amounts = useShiftAmounts({ selectedMarketInfo, - selectedMarketText, selectedToken, toMarketInfo, - toMarketText, toToken, + selectedMarketText, + toMarketText, + focusedInput, uiFeeFactor, - ]); - - const { fees, executionFee } = useMemo(() => { - if (!gasLimits || gasPrice === undefined || !tokensData || !amounts) { - return {}; - } - - const basisUsd = amounts.fromTokenUsd; - - const swapPriceImpact = getFeeItem(amounts.swapPriceImpactDeltaUsd, basisUsd); - const uiFee = getFeeItem(amounts.uiFeeUsd * -1n, basisUsd, { - shouldRoundUp: true, - }); - const shiftFee = getFeeItem(0n, basisUsd); - - const totalFees = getTotalFeeItem([swapPriceImpact, uiFee].filter(Boolean) as FeeItem[]); - const fees: GmSwapFees = { - swapPriceImpact, - totalFees, - uiFee, - shiftFee, - }; - - const gasLimit = estimateExecuteShiftGasLimit(gasLimits, { - callbackGasLimit: 0n, - }); - - const oraclePriceCount = estimateShiftOraclePriceCount(); - - const executionFee = getExecutionFee(chainId, gasLimits, tokensData, gasLimit, gasPrice, oraclePriceCount); + }); - return { - fees, - executionFee, - }; - }, [amounts, chainId, gasLimits, gasPrice, tokensData]); + const { fees, executionFee } = useShiftFees({ gasLimits, gasPrice, tokensData, amounts, chainId }); const isHighPriceImpact = (fees?.swapPriceImpact?.deltaUsd ?? 0) < 0 && bigMath.abs(fees?.swapPriceImpact?.bps ?? 0n) >= HIGH_PRICE_IMPACT_BPS; - const submitState = useMemo(() => { - if (!account) { - return { - text: t`Connect Wallet`, - onSubmit: () => openConnectModal?.(), - }; - } - - const commonError = getCommonError({ - chainId, - isConnected: true, - hasOutdatedUi, - })[0]; - - const shiftError = getGmShiftError({ - fromMarketInfo: selectedMarketInfo, - fromToken: selectedToken, - fromTokenAmount: amounts?.fromTokenAmount, - fromTokenUsd: amounts?.fromTokenUsd, - fromLongTokenAmount: amounts?.fromLongTokenAmount, - fromShortTokenAmount: amounts?.fromShortTokenAmount, - toMarketInfo: toMarketInfo, - toToken: toToken, - toTokenAmount: amounts?.toTokenAmount, - fees, - isHighPriceImpact: isHighPriceImpact, - isHighPriceImpactAccepted, - priceImpactUsd: amounts?.swapPriceImpactDeltaUsd, - })[0]; - - const error = commonError || shiftError; - - const onSubmit = () => { - setIsConfirmationBoxVisible(true); - }; - - if (error) { - return { - text: error, - error, - isDisabled: !shouldDisableValidationForTesting, - onSubmit, - }; - } - - return { - text: t`Shift GM`, - onSubmit, - }; - }, [ - account, - chainId, - hasOutdatedUi, + const submitState = useShiftSubmitState({ selectedMarketInfo, selectedToken, - amounts?.fromTokenAmount, - amounts?.fromTokenUsd, - amounts?.fromLongTokenAmount, - amounts?.fromShortTokenAmount, - amounts?.toTokenAmount, - amounts?.swapPriceImpactDeltaUsd, + amounts, toMarketInfo, toToken, fees, isHighPriceImpact, isHighPriceImpactAccepted, - openConnectModal, + setIsConfirmationBoxVisible, shouldDisableValidationForTesting, - ]); + }); useUpdateMarkets({ marketsInfoData, @@ -258,28 +124,7 @@ export function GmShiftBox({ setToMarketAddress, }); - useEffect( - function updateTokens() { - if (!amounts || !selectedToken || !toToken) { - return; - } - - if (focusedInput === "selectedMarket") { - if (amounts.toTokenAmount === 0n) { - setToMarketText(""); - } else { - setToMarketText(formatAmountFree(amounts.toTokenAmount, toToken.decimals)); - } - } else { - if (amounts.fromTokenAmount === 0n) { - setSelectedMarketText(""); - } else { - setSelectedMarketText(formatAmountFree(amounts.fromTokenAmount, selectedToken.decimals)); - } - } - }, - [amounts, focusedInput, selectedToken, toToken] - ); + useUpdateTokens({ amounts, selectedToken, toToken, focusedInput, setToMarketText, setSelectedMarketText }); useUpdateByQueryParams({ onSelectMarket, diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAmounts.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAmounts.tsx new file mode 100644 index 0000000000..6d37bd903a --- /dev/null +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAmounts.tsx @@ -0,0 +1,68 @@ +import { useMemo } from "react"; + +import type { MarketInfo } from "domain/synthetics/markets/types"; +import type { TokenData } from "domain/synthetics/tokens/types"; +import { type ShiftAmounts, getShiftAmounts } from "domain/synthetics/trade/utils/shift"; +import { parseValue } from "lib/numbers"; + +export function useShiftAmounts({ + selectedMarketInfo, + selectedToken, + toMarketInfo, + toToken, + selectedMarketText, + toMarketText, + focusedInput, + uiFeeFactor, +}: { + selectedMarketInfo: MarketInfo | undefined; + selectedToken: TokenData | undefined; + toMarketInfo: MarketInfo | undefined; + toToken: TokenData | undefined; + selectedMarketText: string; + toMarketText: string; + focusedInput: string | undefined; + uiFeeFactor: bigint; +}): ShiftAmounts | undefined { + return useMemo(() => { + if (!selectedMarketInfo || !selectedToken || !toMarketInfo || !toToken) { + return; + } + + let fromTokenAmount = 0n; + try { + fromTokenAmount = parseValue(selectedMarketText, selectedToken.decimals) ?? 0n; + } catch { + // pass + } + + let toTokenAmount = 0n; + try { + toTokenAmount = parseValue(toMarketText, toToken.decimals) ?? 0n; + } catch { + // pass + } + + const amounts = getShiftAmounts({ + fromMarketInfo: selectedMarketInfo, + fromToken: selectedToken, + fromTokenAmount, + toMarketInfo, + toToken: toToken, + toTokenAmount, + strategy: focusedInput === "selectedMarket" ? "byFromToken" : "byToToken", + uiFeeFactor, + }); + + return amounts; + }, [ + focusedInput, + selectedMarketInfo, + selectedMarketText, + selectedToken, + toMarketInfo, + toMarketText, + toToken, + uiFeeFactor, + ]); +} diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftFees.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftFees.tsx new file mode 100644 index 0000000000..d0175c3432 --- /dev/null +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftFees.tsx @@ -0,0 +1,64 @@ +import { useMemo } from "react"; + +import { + type ExecutionFee, + type FeeItem, + type GasLimitsConfig, + estimateExecuteShiftGasLimit, + getExecutionFee, + getFeeItem, + getTotalFeeItem, +} from "domain/synthetics/fees"; +import { estimateShiftOraclePriceCount } from "domain/synthetics/fees/utils/estimateOraclePriceCount"; +import type { TokensData } from "domain/synthetics/tokens/types"; +import type { GmSwapFees } from "domain/synthetics/trade/types"; +import type { ShiftAmounts } from "domain/synthetics/trade/utils/shift"; + +export function useShiftFees({ + gasLimits, + gasPrice, + tokensData, + amounts, + chainId, +}: { + gasLimits: GasLimitsConfig | undefined; + gasPrice: bigint | undefined; + tokensData: TokensData | undefined; + amounts: ShiftAmounts | undefined; + chainId: number; +}): { fees?: GmSwapFees; executionFee?: ExecutionFee } { + return useMemo(() => { + if (!gasLimits || gasPrice === undefined || !tokensData || !amounts) { + return {}; + } + + const basisUsd = amounts.fromTokenUsd; + + const swapPriceImpact = getFeeItem(amounts.swapPriceImpactDeltaUsd, basisUsd); + const uiFee = getFeeItem(amounts.uiFeeUsd * -1n, basisUsd, { + shouldRoundUp: true, + }); + const shiftFee = getFeeItem(0n, basisUsd); + + const totalFees = getTotalFeeItem([swapPriceImpact, uiFee].filter(Boolean) as FeeItem[]); + const fees: GmSwapFees = { + swapPriceImpact, + totalFees, + uiFee, + shiftFee, + }; + + const gasLimit = estimateExecuteShiftGasLimit(gasLimits, { + callbackGasLimit: 0n, + }); + + const oraclePriceCount = estimateShiftOraclePriceCount(); + + const executionFee = getExecutionFee(chainId, gasLimits, tokensData, gasLimit, gasPrice, oraclePriceCount); + + return { + fees, + executionFee, + }; + }, [amounts, chainId, gasLimits, gasPrice, tokensData]); +} diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx new file mode 100644 index 0000000000..1dfd068bb5 --- /dev/null +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx @@ -0,0 +1,113 @@ +import { t } from "@lingui/macro"; +import { useConnectModal } from "@rainbow-me/rainbowkit"; +import { useMemo, type Dispatch, type SetStateAction } from "react"; + +import { selectAccount, selectChainId } from "context/SyntheticsStateContext/selectors/globalSelectors"; +import { useSelector } from "context/SyntheticsStateContext/utils"; +import { useHasOutdatedUi } from "domain/legacy"; +import type { MarketInfo } from "domain/synthetics/markets/types"; +import type { TokenData } from "domain/synthetics/tokens/types"; +import type { GmSwapFees } from "domain/synthetics/trade/types"; +import type { ShiftAmounts } from "domain/synthetics/trade/utils/shift"; +import { getCommonError, getGmShiftError } from "domain/synthetics/trade/utils/validation"; + +export function useShiftSubmitState({ + selectedMarketInfo, + selectedToken, + amounts, + toMarketInfo, + toToken, + fees, + isHighPriceImpact, + isHighPriceImpactAccepted, + setIsConfirmationBoxVisible, + shouldDisableValidationForTesting, +}: { + selectedMarketInfo: MarketInfo | undefined; + selectedToken: TokenData | undefined; + amounts: ShiftAmounts | undefined; + toMarketInfo: MarketInfo | undefined; + toToken: TokenData | undefined; + fees: GmSwapFees | undefined; + isHighPriceImpact: boolean; + isHighPriceImpactAccepted: boolean; + setIsConfirmationBoxVisible: Dispatch>; + shouldDisableValidationForTesting: boolean; +}) { + const chainId = useSelector(selectChainId); + const account = useSelector(selectAccount); + const { data: hasOutdatedUi } = useHasOutdatedUi(); + + const { openConnectModal } = useConnectModal(); + + return useMemo(() => { + if (!account) { + return { + text: t`Connect Wallet`, + onSubmit: () => openConnectModal?.(), + }; + } + + const commonError = getCommonError({ + chainId, + isConnected: true, + hasOutdatedUi, + })[0]; + + const shiftError = getGmShiftError({ + fromMarketInfo: selectedMarketInfo, + fromToken: selectedToken, + fromTokenAmount: amounts?.fromTokenAmount, + fromTokenUsd: amounts?.fromTokenUsd, + fromLongTokenAmount: amounts?.fromLongTokenAmount, + fromShortTokenAmount: amounts?.fromShortTokenAmount, + toMarketInfo: toMarketInfo, + toToken: toToken, + toTokenAmount: amounts?.toTokenAmount, + fees, + isHighPriceImpact: isHighPriceImpact, + isHighPriceImpactAccepted, + priceImpactUsd: amounts?.swapPriceImpactDeltaUsd, + })[0]; + + const error = commonError || shiftError; + + const onSubmit = () => { + setIsConfirmationBoxVisible(true); + }; + + if (error) { + return { + text: error, + error, + isDisabled: !shouldDisableValidationForTesting, + onSubmit, + }; + } + + return { + text: t`Shift GM`, + onSubmit, + }; + }, [ + account, + chainId, + hasOutdatedUi, + selectedMarketInfo, + selectedToken, + amounts?.fromTokenAmount, + amounts?.fromTokenUsd, + amounts?.fromLongTokenAmount, + amounts?.fromShortTokenAmount, + amounts?.toTokenAmount, + amounts?.swapPriceImpactDeltaUsd, + toMarketInfo, + toToken, + fees, + isHighPriceImpact, + isHighPriceImpactAccepted, + openConnectModal, + setIsConfirmationBoxVisible, + shouldDisableValidationForTesting, + ]); +} diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useUpdateTokens.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useUpdateTokens.tsx new file mode 100644 index 0000000000..8ae73859ca --- /dev/null +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useUpdateTokens.tsx @@ -0,0 +1,44 @@ +import { type Dispatch, type SetStateAction, useEffect } from "react"; + +import type { TokenData } from "domain/synthetics/tokens/types"; +import type { ShiftAmounts } from "domain/synthetics/trade/utils/shift"; +import { formatAmountFree } from "lib/numbers"; + +export function useUpdateTokens({ + amounts, + selectedToken, + toToken, + focusedInput, + setToMarketText, + setSelectedMarketText, +}: { + amounts: ShiftAmounts | undefined; + selectedToken: TokenData | undefined; + toToken: TokenData | undefined; + focusedInput: string | undefined; + setToMarketText: Dispatch>; + setSelectedMarketText: Dispatch>; +}): void { + useEffect( + function updateTokens() { + if (!amounts || !selectedToken || !toToken) { + return; + } + + if (focusedInput === "selectedMarket") { + if (amounts.toTokenAmount === 0n) { + setToMarketText(""); + } else { + setToMarketText(formatAmountFree(amounts.toTokenAmount, toToken.decimals)); + } + } else { + if (amounts.fromTokenAmount === 0n) { + setSelectedMarketText(""); + } else { + setSelectedMarketText(formatAmountFree(amounts.fromTokenAmount, selectedToken.decimals)); + } + } + }, + [amounts, focusedInput, selectedToken, setSelectedMarketText, setToMarketText, toToken] + ); +} diff --git a/src/domain/synthetics/trade/utils/shift.ts b/src/domain/synthetics/trade/utils/shift.ts index 03c3d93ea6..cbe40c886c 100644 --- a/src/domain/synthetics/trade/utils/shift.ts +++ b/src/domain/synthetics/trade/utils/shift.ts @@ -5,7 +5,7 @@ import type { TokenData } from "domain/synthetics/tokens/types"; import { getDepositAmounts } from "./deposit"; import { getWithdrawalAmounts } from "./withdrawal"; -type ShiftAmounts = { +export type ShiftAmounts = { fromTokenAmount: bigint; fromTokenUsd: bigint; fromLongTokenAmount: bigint; From c7772acde8069e3bee355363cf63c41b7be277f0 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 6 Aug 2024 15:05:31 +0000 Subject: [PATCH 31/62] Sync translations --- src/locales/de/messages.po | 4 ++-- src/locales/en/messages.po | 4 ++-- src/locales/es/messages.po | 4 ++-- src/locales/fr/messages.po | 4 ++-- src/locales/ja/messages.po | 4 ++-- src/locales/ko/messages.po | 4 ++-- src/locales/pseudo/messages.po | 4 ++-- src/locales/ru/messages.po | 4 ++-- src/locales/zh/messages.po | 4 ++-- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/locales/de/messages.po b/src/locales/de/messages.po index 5a9cb3f2c0..c37c9903d9 100644 --- a/src/locales/de/messages.po +++ b/src/locales/de/messages.po @@ -1203,7 +1203,7 @@ msgstr "Wenn du eine bestehende Position hast, wird die Position für {0} USD ge msgid "Transfer Submitted" msgstr "Transfer übermittelt" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Shift GM" msgstr "" @@ -1788,7 +1788,7 @@ msgstr "" #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js #: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index 7a5bd9dba1..1cf22b7620 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -1203,7 +1203,7 @@ msgstr "If you have an existing position, the position will be closed at {0} USD msgid "Transfer Submitted" msgstr "Transfer Submitted" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Shift GM" msgstr "Shift GM" @@ -1788,7 +1788,7 @@ msgstr "We value your experience and insights and invite you to participate in a #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js #: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js diff --git a/src/locales/es/messages.po b/src/locales/es/messages.po index d9ce7f5b47..9a78d1e8a8 100644 --- a/src/locales/es/messages.po +++ b/src/locales/es/messages.po @@ -1203,7 +1203,7 @@ msgstr "Si tienes una posición existente, la posición se cerrará a {0} USD.<0 msgid "Transfer Submitted" msgstr "Transferencia Enviada" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Shift GM" msgstr "" @@ -1788,7 +1788,7 @@ msgstr "" #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js #: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js diff --git a/src/locales/fr/messages.po b/src/locales/fr/messages.po index a1b5b9c401..ad2e14a648 100644 --- a/src/locales/fr/messages.po +++ b/src/locales/fr/messages.po @@ -1203,7 +1203,7 @@ msgstr "Si vous avez une position existante, la position sera fermée à {0} USD msgid "Transfer Submitted" msgstr "Transfert soumis" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Shift GM" msgstr "" @@ -1788,7 +1788,7 @@ msgstr "" #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js #: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js diff --git a/src/locales/ja/messages.po b/src/locales/ja/messages.po index b1a4b680b9..7e6fe9ec99 100644 --- a/src/locales/ja/messages.po +++ b/src/locales/ja/messages.po @@ -1203,7 +1203,7 @@ msgstr "もし現在ポジションがある場合、そのポジションは{0} msgid "Transfer Submitted" msgstr "移転申し込み完了" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Shift GM" msgstr "" @@ -1788,7 +1788,7 @@ msgstr "" #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js #: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js diff --git a/src/locales/ko/messages.po b/src/locales/ko/messages.po index d9eb0cdb7d..3865560040 100644 --- a/src/locales/ko/messages.po +++ b/src/locales/ko/messages.po @@ -1203,7 +1203,7 @@ msgstr "포지션이 이미 존재한다면, 해당 포지션은 {0} USD에 종 msgid "Transfer Submitted" msgstr "전송 제출 완료" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Shift GM" msgstr "" @@ -1788,7 +1788,7 @@ msgstr "" #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js #: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js diff --git a/src/locales/pseudo/messages.po b/src/locales/pseudo/messages.po index 0f3c34f7fb..99848a9501 100644 --- a/src/locales/pseudo/messages.po +++ b/src/locales/pseudo/messages.po @@ -1203,7 +1203,7 @@ msgstr "" msgid "Transfer Submitted" msgstr "" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Shift GM" msgstr "" @@ -1788,7 +1788,7 @@ msgstr "" #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js #: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js diff --git a/src/locales/ru/messages.po b/src/locales/ru/messages.po index 154d70f25c..c61006d2a2 100644 --- a/src/locales/ru/messages.po +++ b/src/locales/ru/messages.po @@ -1203,7 +1203,7 @@ msgstr "Если у вас есть существующая позиция, п msgid "Transfer Submitted" msgstr "Перевод Подтверждён" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Shift GM" msgstr "" @@ -1788,7 +1788,7 @@ msgstr "" #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js #: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js diff --git a/src/locales/zh/messages.po b/src/locales/zh/messages.po index 98e384d6aa..ae1d1deeb1 100644 --- a/src/locales/zh/messages.po +++ b/src/locales/zh/messages.po @@ -1203,7 +1203,7 @@ msgstr "如果你有一个现有的头寸,它将在{0}美元被关闭。<0/><1 msgid "Transfer Submitted" msgstr "送出转移" -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx msgid "Shift GM" msgstr "" @@ -1788,7 +1788,7 @@ msgstr "" #: src/components/Referrals/AddAffiliateCode.js #: src/components/Referrals/JoinReferralCode.js #: src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx -#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +#: src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftSubmitState.tsx #: src/components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList.tsx #: src/domain/synthetics/trade/utils/validation.ts #: src/pages/Stake/StakeV1.js From ea51dc32a69198fcd72bb4c335d9d1d9c037fbc4 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 6 Aug 2024 15:10:04 +0000 Subject: [PATCH 32/62] Refactor getShiftAvailableMarkets to use getMarketPoolName for longShortKey --- .../GmSwapBox/GmShiftBox/getShiftAvailableMarkets.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/getShiftAvailableMarkets.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/getShiftAvailableMarkets.tsx index e2e159514a..a4edca3caf 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/getShiftAvailableMarkets.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/getShiftAvailableMarkets.tsx @@ -1,4 +1,5 @@ import type { MarketInfo } from "domain/synthetics/markets/types"; +import { getMarketPoolName } from "domain/synthetics/markets/utils"; import { EMPTY_ARRAY } from "lib/objects"; export function getShiftAvailableMarkets({ markets }: { markets: MarketInfo[] }): MarketInfo[] { @@ -9,7 +10,7 @@ export function getShiftAvailableMarkets({ markets }: { markets: MarketInfo[] }) const shiftGroups: { [longShortKey: string]: MarketInfo[] } = {}; for (const marketInfo of markets) { - const longShortKey = `${marketInfo.longTokenAddress}-${marketInfo.shortTokenAddress}`; + const longShortKey = getMarketPoolName(marketInfo); if (!shiftGroups[longShortKey]) { shiftGroups[longShortKey] = []; @@ -21,9 +22,10 @@ export function getShiftAvailableMarkets({ markets }: { markets: MarketInfo[] }) const availableMarkets: MarketInfo[] = []; for (const marketInfo of markets) { - const longShortKey = `${marketInfo.longTokenAddress}-${marketInfo.shortTokenAddress}`; + const longShortKey = getMarketPoolName(marketInfo); - if (shiftGroups[longShortKey].length > 1) { + const multipleRelatedMarkets = shiftGroups[longShortKey].length > 1; + if (multipleRelatedMarkets) { availableMarkets.push(marketInfo); } } From a84f56826468048e3eafe25f154bb9c839a64a81 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 6 Aug 2024 16:12:58 +0000 Subject: [PATCH 33/62] Refactor shiftSelectors and globalSelectors in SyntheticsStateContext --- src/components/Synthetics/GmList/GmList.tsx | 4 +-- .../GmSwapBox/GmShiftBox/GmShiftBox.tsx | 6 ++-- .../GmShiftBox/useShiftAvailableMarkets.tsx | 33 ------------------- .../selectors/globalSelectors.ts | 1 + .../selectSortedMarketInfoByIndexToken.tsx | 16 +++++++++ .../selectors/shiftSelectors.ts | 11 +++++++ 6 files changed, 33 insertions(+), 38 deletions(-) delete mode 100644 src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableMarkets.tsx create mode 100644 src/context/SyntheticsStateContext/selectors/selectSortedMarketInfoByIndexToken.tsx create mode 100644 src/context/SyntheticsStateContext/selectors/shiftSelectors.ts diff --git a/src/components/Synthetics/GmList/GmList.tsx b/src/components/Synthetics/GmList/GmList.tsx index bdbf0777d8..7af8647a32 100644 --- a/src/components/Synthetics/GmList/GmList.tsx +++ b/src/components/Synthetics/GmList/GmList.tsx @@ -10,6 +10,7 @@ import { getNormalizedTokenSymbol } from "config/tokens"; import { useSettings } from "context/SettingsContext/SettingsContextProvider"; import { useMarketsInfoData, useTokensData } from "context/SyntheticsStateContext/hooks/globalsHooks"; import { selectChainId } from "context/SyntheticsStateContext/selectors/globalSelectors"; +import { selectShiftAvailableMarkets } from "context/SyntheticsStateContext/selectors/shiftSelectors"; import { useSelector } from "context/SyntheticsStateContext/utils"; import { MarketTokensAPRData, @@ -44,7 +45,6 @@ import TokenIcon from "components/TokenIcon/TokenIcon"; import TooltipWithPortal from "components/Tooltip/TooltipWithPortal"; import GmAssetDropdown from "../GmAssetDropdown/GmAssetDropdown"; import { ExchangeTd, ExchangeTh, ExchangeTheadTr, ExchangeTr } from "../OrderList/ExchangeTable"; -import { useShiftAvailableMarkets } from "../GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableMarkets"; type Props = { marketsTokensApyData: MarketTokensAPRData | undefined; @@ -69,7 +69,7 @@ export function GmList({ marketsTokensApyData, marketsTokensIncentiveAprData, sh const daysConsidered = useDaysConsideredInMarketsApr(); const { orderBy, direction, getSorterProps } = useSorterHandlers(); const [searchText, setSearchText] = useState(""); - const shiftAvailableMarkets = useShiftAvailableMarkets(); + const shiftAvailableMarkets = useSelector(selectShiftAvailableMarkets); const shiftAvailableMarketAddressSet = useMemo( () => new Set(shiftAvailableMarkets.map((m) => m.marketTokenAddress)), [shiftAvailableMarkets] diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx index ac984b992b..ce18247562 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx @@ -9,6 +9,7 @@ import { selectGasLimits, selectGasPrice, } from "context/SyntheticsStateContext/selectors/globalSelectors"; +import { selectShiftAvailableMarkets } from "context/SyntheticsStateContext/selectors/shiftSelectors"; import { useSelector } from "context/SyntheticsStateContext/utils"; import { MarketInfo, getMarketIndexName } from "domain/synthetics/markets"; import { useMarketTokensData } from "domain/synthetics/markets/useMarketTokensData"; @@ -19,7 +20,6 @@ import { formatAmountFree, formatTokenAmount, formatUsd } from "lib/numbers"; import { getByKey } from "lib/objects"; import { Mode, Operation } from "../types"; import { useUpdateByQueryParams } from "../useUpdateByQueryParams"; -import { useShiftAvailableMarkets } from "./useShiftAvailableMarkets"; import { useShiftAvailableRelatedMarkets } from "./useShiftAvailableRelatedMarkets"; import { useUpdateMarkets } from "./useUpdateMarkets"; @@ -32,10 +32,10 @@ import { GmConfirmationBox } from "../../GmConfirmationBox/GmConfirmationBox"; import { GmFees } from "../../GmFees/GmFees"; import { HighPriceImpactRow } from "../HighPriceImpactRow"; import { Swap } from "../Swap"; -import { useUpdateTokens } from "./useUpdateTokens"; import { useShiftAmounts } from "./useShiftAmounts"; import { useShiftFees } from "./useShiftFees"; import { useShiftSubmitState } from "./useShiftSubmitState"; +import { useUpdateTokens } from "./useUpdateTokens"; export function GmShiftBox({ selectedMarketAddress, @@ -68,7 +68,7 @@ export function GmShiftBox({ marketsInfoData, depositMarketTokensData ); - const shiftAvailableMarkets = useShiftAvailableMarkets(); + const shiftAvailableMarkets = useSelector(selectShiftAvailableMarkets); const shiftAvailableRelatedMarkets = useShiftAvailableRelatedMarkets( marketsInfoData, sortedMarketsInfoByIndexToken, diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableMarkets.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableMarkets.tsx deleted file mode 100644 index ace398b6ad..0000000000 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableMarkets.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import type { SyntheticsState } from "context/SyntheticsStateContext/SyntheticsStateContextProvider"; -import { selectMarketsInfoData } from "context/SyntheticsStateContext/selectors/globalSelectors"; -import { createSelector, useSelector } from "context/SyntheticsStateContext/utils"; -import type { MarketInfo } from "domain/synthetics/markets/types"; -import { sortMarketsWithIndexToken } from "domain/synthetics/trade/useSortedPoolsWithIndexToken"; - -import { getShiftAvailableMarkets } from "./getShiftAvailableMarkets"; - -const selectMarketTokensForDepositData = (s: SyntheticsState) => s.globals.depositMarketTokensData; - -const selectSortedMarketInfoByIndexToken = createSelector((q) => { - const marketsInfoData = q(selectMarketsInfoData); - - const depositMarketTokensData = q(selectMarketTokensForDepositData); - - const sortedMarketsInfoByIndexToken = sortMarketsWithIndexToken(marketsInfoData, depositMarketTokensData); - - return sortedMarketsInfoByIndexToken.marketsInfo; -}); - -const selectShiftAvailableMarkets = createSelector((q) => { - const sortedMarketsInfoByIndexToken = q(selectSortedMarketInfoByIndexToken); - - return getShiftAvailableMarkets({ - markets: sortedMarketsInfoByIndexToken, - }); -}); - -export function useShiftAvailableMarkets() { - const shiftAvailableMarkets: MarketInfo[] = useSelector(selectShiftAvailableMarkets); - - return shiftAvailableMarkets; -} diff --git a/src/context/SyntheticsStateContext/selectors/globalSelectors.ts b/src/context/SyntheticsStateContext/selectors/globalSelectors.ts index 39321ea58d..0adeba7a54 100644 --- a/src/context/SyntheticsStateContext/selectors/globalSelectors.ts +++ b/src/context/SyntheticsStateContext/selectors/globalSelectors.ts @@ -13,6 +13,7 @@ export const selectGmMarkets = (s: SyntheticsState) => s.globals.markets.markets export const selectUiFeeFactor = (s: SyntheticsState) => s.globals.uiFeeFactor; export const selectUserReferralInfo = (s: SyntheticsState) => s.globals.userReferralInfo; export const selectChainId = (s: SyntheticsState) => s.globals.chainId; +export const selectDepositMarketTokensData = (s: SyntheticsState) => s.globals.depositMarketTokensData; export const selectMinCollateralUsd = (s: SyntheticsState) => s.globals.positionsConstants.minCollateralUsd; export const selectMinPositionSizeUsd = (s: SyntheticsState) => s.globals.positionsConstants.minPositionSizeUsd; diff --git a/src/context/SyntheticsStateContext/selectors/selectSortedMarketInfoByIndexToken.tsx b/src/context/SyntheticsStateContext/selectors/selectSortedMarketInfoByIndexToken.tsx new file mode 100644 index 0000000000..d613fb9df3 --- /dev/null +++ b/src/context/SyntheticsStateContext/selectors/selectSortedMarketInfoByIndexToken.tsx @@ -0,0 +1,16 @@ +import { + selectDepositMarketTokensData, + selectMarketsInfoData, +} from "context/SyntheticsStateContext/selectors/globalSelectors"; +import { createSelector } from "context/SyntheticsStateContext/utils"; +import { sortMarketsWithIndexToken } from "domain/synthetics/trade/useSortedPoolsWithIndexToken"; + +export const selectSortedMarketInfoByIndexToken = createSelector((q) => { + const marketsInfoData = q(selectMarketsInfoData); + + const depositMarketTokensData = q(selectDepositMarketTokensData); + + const sortedMarketsInfoByIndexToken = sortMarketsWithIndexToken(marketsInfoData, depositMarketTokensData); + + return sortedMarketsInfoByIndexToken.marketsInfo; +}); diff --git a/src/context/SyntheticsStateContext/selectors/shiftSelectors.ts b/src/context/SyntheticsStateContext/selectors/shiftSelectors.ts new file mode 100644 index 0000000000..2235fed8c3 --- /dev/null +++ b/src/context/SyntheticsStateContext/selectors/shiftSelectors.ts @@ -0,0 +1,11 @@ +import { getShiftAvailableMarkets } from "components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/getShiftAvailableMarkets"; +import { createSelector } from "../utils"; +import { selectSortedMarketInfoByIndexToken } from "./selectSortedMarketInfoByIndexToken"; + +export const selectShiftAvailableMarkets = createSelector((q) => { + const sortedMarketsInfoByIndexToken = q(selectSortedMarketInfoByIndexToken); + + return getShiftAvailableMarkets({ + markets: sortedMarketsInfoByIndexToken, + }); +}); From d64efc03b59b63b0fc0315b9ceaaf242868fc498 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 6 Aug 2024 16:35:19 +0000 Subject: [PATCH 34/62] Enhance shift toast and modal messages --- .../GmSwap/GmConfirmationBox/GmConfirmationBox.tsx | 8 +++++--- .../Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx | 1 + .../StatusNotification/GmStatusNotification.tsx | 8 +++++--- src/locales/de/messages.po | 8 ++++---- src/locales/en/messages.po | 8 ++++---- src/locales/es/messages.po | 8 ++++---- src/locales/fr/messages.po | 8 ++++---- src/locales/ja/messages.po | 8 ++++---- src/locales/ko/messages.po | 8 ++++---- src/locales/pseudo/messages.po | 8 ++++---- src/locales/ru/messages.po | 8 ++++---- src/locales/zh/messages.po | 8 ++++---- 12 files changed, 47 insertions(+), 42 deletions(-) diff --git a/src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx b/src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx index 8a8a7105af..d41096a967 100644 --- a/src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +++ b/src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx @@ -548,9 +548,11 @@ export function GmConfirmationBox({
{tokensToApprove.map((address) => { const token = getTokenData(tokensData, address)!; - const marketTokenData = - (address === marketToken?.address || address === fromMarketToken?.address) && - getByKey(marketsInfoData, marketToken?.address); + let marketTokenData = + address === marketToken?.address && getByKey(marketsInfoData, marketToken?.address); + if (operation === Operation.Shift) { + marketTokenData = getByKey(marketsInfoData, fromMarketToken?.address); + } return (
diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx index 7ec9f5804e..77a417a016 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/HighPriceImpactRow.tsx @@ -1,4 +1,5 @@ import { t, Trans } from "@lingui/macro"; + import Checkbox from "components/Checkbox/Checkbox"; import { ExchangeInfo } from "components/Exchange/ExchangeInfo"; import Tooltip from "components/Tooltip/Tooltip"; diff --git a/src/components/Synthetics/StatusNotification/GmStatusNotification.tsx b/src/components/Synthetics/StatusNotification/GmStatusNotification.tsx index c048ff49fb..fa6a0494b9 100644 --- a/src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +++ b/src/components/Synthetics/StatusNotification/GmStatusNotification.tsx @@ -186,12 +186,14 @@ export function GmStatusNotification({ return t`Unknown shift GM order`; } - // const fromToken = getByKey(tokensData, pendingShiftData.fromMarketTokenAddress); - // const toToken = getByKey(tokensData, pendingShiftData.toMarketTokenAddress); + const fromMarketInfo = getByKey(marketsInfoData, pendingShiftData.fromMarket); + const fromIndexName = fromMarketInfo ? getMarketIndexName(fromMarketInfo) : ""; + const toMarketInfo = getByKey(marketsInfoData, pendingShiftData.toMarket); + const toIndexName = toMarketInfo ? getMarketIndexName(toMarketInfo) : ""; return ( -
Shifting GM
+ Shifting GM from {fromIndexName} to {toIndexName}
); } diff --git a/src/locales/de/messages.po b/src/locales/de/messages.po index c37c9903d9..a682d63551 100644 --- a/src/locales/de/messages.po +++ b/src/locales/de/messages.po @@ -1550,10 +1550,6 @@ msgstr "" msgid "Max {0} out" msgstr "Max. {0} heraus" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "<0>Shifting GM" -msgstr "" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "" @@ -7682,6 +7678,10 @@ msgstr "Genehmigung wurde aufgehoben" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from {fromIndexName} to {toIndexName}" +msgstr "" + #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "Erhöhen aktiv: {0}, ausgeführt: {1}, abgebrochen: {2}" diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index 1cf22b7620..a6bc1eeff5 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -1550,10 +1550,6 @@ msgstr "Pair" msgid "Max {0} out" msgstr "Max {0} out" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "<0>Shifting GM" -msgstr "<0>Shifting GM" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "Updating Subaccount" @@ -7688,6 +7684,10 @@ msgstr "Approval was cancelled" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from {fromIndexName} to {toIndexName}" +msgstr "Shifting GM from {fromIndexName} to {toIndexName}" + #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "Increase active: {0}, executed: {1}, cancelled: {2}" diff --git a/src/locales/es/messages.po b/src/locales/es/messages.po index 9a78d1e8a8..3e46278e05 100644 --- a/src/locales/es/messages.po +++ b/src/locales/es/messages.po @@ -1550,10 +1550,6 @@ msgstr "" msgid "Max {0} out" msgstr "Máx. {0} fuera" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "<0>Shifting GM" -msgstr "" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "" @@ -7682,6 +7678,10 @@ msgstr "Aprobación cancelada" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from {fromIndexName} to {toIndexName}" +msgstr "" + #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "Incrementar activo: {0}, ejecutado: {1}, cancelado: {2}" diff --git a/src/locales/fr/messages.po b/src/locales/fr/messages.po index ad2e14a648..4bd28f3654 100644 --- a/src/locales/fr/messages.po +++ b/src/locales/fr/messages.po @@ -1550,10 +1550,6 @@ msgstr "" msgid "Max {0} out" msgstr "Sortir en {0} max" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "<0>Shifting GM" -msgstr "" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "" @@ -7682,6 +7678,10 @@ msgstr "L'approbation a été annulée" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from {fromIndexName} to {toIndexName}" +msgstr "" + #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "Augmentation active: {0}, exécuté: {1}, annulé: {2}" diff --git a/src/locales/ja/messages.po b/src/locales/ja/messages.po index 7e6fe9ec99..b1e5398add 100644 --- a/src/locales/ja/messages.po +++ b/src/locales/ja/messages.po @@ -1550,10 +1550,6 @@ msgstr "" msgid "Max {0} out" msgstr "最大 {0} out" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "<0>Shifting GM" -msgstr "" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "" @@ -7682,6 +7678,10 @@ msgstr "承認はキャンセルされました" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from {fromIndexName} to {toIndexName}" +msgstr "" + #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "増加 アクティブ: {0} 執行済: {1} キャンセル済: {2}" diff --git a/src/locales/ko/messages.po b/src/locales/ko/messages.po index 3865560040..c05254f32f 100644 --- a/src/locales/ko/messages.po +++ b/src/locales/ko/messages.po @@ -1550,10 +1550,6 @@ msgstr "" msgid "Max {0} out" msgstr "최대 {0} out" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "<0>Shifting GM" -msgstr "" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "" @@ -7682,6 +7678,10 @@ msgstr "제출 취소되었습니다" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from {fromIndexName} to {toIndexName}" +msgstr "" + #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "중가 액티브: {0}, 실행 완료: {1}, 취소 완료: {2}" diff --git a/src/locales/pseudo/messages.po b/src/locales/pseudo/messages.po index 99848a9501..7cfad90962 100644 --- a/src/locales/pseudo/messages.po +++ b/src/locales/pseudo/messages.po @@ -1550,10 +1550,6 @@ msgstr "" msgid "Max {0} out" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "<0>Shifting GM" -msgstr "" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "" @@ -7682,6 +7678,10 @@ msgstr "" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from {fromIndexName} to {toIndexName}" +msgstr "" + #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "" diff --git a/src/locales/ru/messages.po b/src/locales/ru/messages.po index c61006d2a2..73cecfa201 100644 --- a/src/locales/ru/messages.po +++ b/src/locales/ru/messages.po @@ -1550,10 +1550,6 @@ msgstr "" msgid "Max {0} out" msgstr "Максимальный {0} выход" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "<0>Shifting GM" -msgstr "" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "" @@ -7682,6 +7678,10 @@ msgstr "Одобрение было отменено" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from {fromIndexName} to {toIndexName}" +msgstr "" + #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "Увеличение активно: {0}, выполнено: {1}, отменено: {2}" diff --git a/src/locales/zh/messages.po b/src/locales/zh/messages.po index ae1d1deeb1..13cdb7889f 100644 --- a/src/locales/zh/messages.po +++ b/src/locales/zh/messages.po @@ -1550,10 +1550,6 @@ msgstr "" msgid "Max {0} out" msgstr "最高{0}以外" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "<0>Shifting GM" -msgstr "" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Updating Subaccount" msgstr "" @@ -7682,6 +7678,10 @@ msgstr "核准已被取消" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from {fromIndexName} to {toIndexName}" +msgstr "" + #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "提升活跃度:{0},执行。{1},取消了。{2}" From a7aab8fd24e5045a0e87652c89aa233a077ac474 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 6 Aug 2024 18:10:52 +0000 Subject: [PATCH 35/62] Shifts market selection changes fallbacks --- .../GmSwapBox/GmShiftBox/GmShiftBox.tsx | 15 +++--- .../useShiftAvailableRelatedMarkets.tsx | 4 +- .../GmSwapBox/GmShiftBox/useUpdateMarkets.tsx | 12 ++--- .../Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx | 47 +++++++++++++++---- src/components/Tab/Tab.css | 14 ++++-- src/pages/MarketPoolsPage/MarketPoolsPage.tsx | 4 +- 6 files changed, 64 insertions(+), 32 deletions(-) diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx index ce18247562..23028e3ac2 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx @@ -1,5 +1,5 @@ import { t } from "@lingui/macro"; -import { Dispatch, SetStateAction, useCallback, useState } from "react"; +import { useCallback, useState } from "react"; import { HIGH_PRICE_IMPACT_BPS } from "config/factors"; import { useSettings } from "context/SettingsContext/SettingsContextProvider"; @@ -19,9 +19,14 @@ import { bigMath } from "lib/bigmath"; import { formatAmountFree, formatTokenAmount, formatUsd } from "lib/numbers"; import { getByKey } from "lib/objects"; import { Mode, Operation } from "../types"; + import { useUpdateByQueryParams } from "../useUpdateByQueryParams"; +import { useShiftAmounts } from "./useShiftAmounts"; import { useShiftAvailableRelatedMarkets } from "./useShiftAvailableRelatedMarkets"; +import { useShiftFees } from "./useShiftFees"; +import { useShiftSubmitState } from "./useShiftSubmitState"; import { useUpdateMarkets } from "./useUpdateMarkets"; +import { useUpdateTokens } from "./useUpdateTokens"; import Button from "components/Button/Button"; import BuyInputSection from "components/BuyInputSection/BuyInputSection"; @@ -32,10 +37,6 @@ import { GmConfirmationBox } from "../../GmConfirmationBox/GmConfirmationBox"; import { GmFees } from "../../GmFees/GmFees"; import { HighPriceImpactRow } from "../HighPriceImpactRow"; import { Swap } from "../Swap"; -import { useShiftAmounts } from "./useShiftAmounts"; -import { useShiftFees } from "./useShiftFees"; -import { useShiftSubmitState } from "./useShiftSubmitState"; -import { useUpdateTokens } from "./useUpdateTokens"; export function GmShiftBox({ selectedMarketAddress, @@ -46,8 +47,8 @@ export function GmShiftBox({ }: { selectedMarketAddress: string | undefined; onSelectMarket: (marketAddress: string) => void; - onSetMode: Dispatch>; - onSetOperation: Dispatch>; + onSetMode: (mode: Mode) => void; + onSetOperation: (operation: Operation) => void; }) { const [toMarketAddress, setToMarketAddress] = useState(undefined); const [selectedMarketText, setSelectedMarketText] = useState(""); diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableRelatedMarkets.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableRelatedMarkets.tsx index 06d1cf589d..d7ba604fbe 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableRelatedMarkets.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useShiftAvailableRelatedMarkets.tsx @@ -9,7 +9,7 @@ export function useShiftAvailableRelatedMarkets( sortedMarketsInfoByIndexToken: MarketInfo[], marketTokenAddress?: string ) { - const shiftAvailableMarkets: MarketInfo[] = useMemo( + const shiftAvailableRelatedMarkets: MarketInfo[] = useMemo( () => getShiftAvailableRelatedMarkets({ marketsInfoData, @@ -19,5 +19,5 @@ export function useShiftAvailableRelatedMarkets( [marketTokenAddress, marketsInfoData, sortedMarketsInfoByIndexToken] ); - return shiftAvailableMarkets; + return shiftAvailableRelatedMarkets; } diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useUpdateMarkets.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useUpdateMarkets.tsx index 5ab10ae05c..7216ecc2cf 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useUpdateMarkets.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useUpdateMarkets.tsx @@ -34,24 +34,20 @@ export function useUpdateMarkets({ const isSelectedMarketValid = Boolean( selectedMarketAddress && - getByKey(marketsInfoData, selectedMarketAddress) && shiftAvailableMarkets.find((market) => market.marketTokenAddress === selectedMarketAddress) ); if (!isSelectedMarketValid) { - const firstMarketInfo = shiftAvailableMarkets[0]; - if (!firstMarketInfo) { - return; - } - onSelectMarket(firstMarketInfo.marketTokenAddress); - newSelectedMarketAddress = firstMarketInfo.marketTokenAddress; + // Parent component should switch tab to withdrawal + return; } const isToMarketAvailable = Boolean(toMarketAddress && getByKey(marketsInfoData, toMarketAddress)); const isToMarketRelated = toMarketInfo?.longTokenAddress === selectedMarketInfo?.longTokenAddress && toMarketInfo?.shortTokenAddress === selectedMarketInfo?.shortTokenAddress; - const isToMarketValid = isToMarketAvailable && isToMarketRelated; + const isToMarketSameAsSelected = toMarketAddress === newSelectedMarketAddress; + const isToMarketValid = isToMarketAvailable && isToMarketRelated && !isToMarketSameAsSelected; if (!isToMarketValid) { const someAvailableMarket = getShiftAvailableRelatedMarkets({ diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx index 384f714f02..885f47e67f 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx @@ -1,15 +1,17 @@ import { msg } from "@lingui/macro"; -import { Dispatch, SetStateAction, useCallback } from "react"; +import { useEffect, useMemo } from "react"; import { useMarketsInfoData } from "context/SyntheticsStateContext/hooks/globalsHooks"; +import { selectShiftAvailableMarkets } from "context/SyntheticsStateContext/selectors/shiftSelectors"; +import { useSelector } from "context/SyntheticsStateContext/utils"; import { useLocalizedMap } from "lib/i18n"; import { getByKey } from "lib/objects"; import { getGmSwapBoxAvailableModes } from "./getGmSwapBoxAvailableModes"; import { Mode, Operation } from "./types"; import Tab from "components/Tab/Tab"; -import { GmShiftBox } from "./GmShiftBox/GmShiftBox"; import { GmSwapBoxDepositWithdrawal } from "./GmDepositWithdrawalBox/GmDepositWithdrawalBox"; +import { GmShiftBox } from "./GmShiftBox/GmShiftBox"; import "./GmSwapBox.scss"; @@ -18,8 +20,8 @@ export type GmSwapBoxProps = { onSelectMarket: (marketAddress: string) => void; operation: Operation; mode: Mode; - onSetMode: Dispatch>; - onSetOperation: Dispatch>; + onSetMode: (mode: Mode) => void; + onSetOperation: (operation: Operation) => void; }; const OPERATION_LABELS = { @@ -41,13 +43,38 @@ export function GmSwapBox(p: GmSwapBoxProps) { const marketsInfoData = useMarketsInfoData(); const marketInfo = getByKey(marketsInfoData, marketAddress); + const shiftAvailableMarkets = useSelector(selectShiftAvailableMarkets); + + const isSelectedMarketShiftAvailable = useMemo(() => { + return Boolean(shiftAvailableMarkets?.find((market) => market.marketTokenAddress === selectedMarketAddress)); + }, [selectedMarketAddress, shiftAvailableMarkets]); + + const availableOperations = useMemo(() => { + return [ + Operation.Deposit, + Operation.Withdrawal, + { + text: Operation.Shift, + disabled: !isSelectedMarketShiftAvailable, + }, + ]; + }, [isSelectedMarketShiftAvailable]); + const availableModes = getGmSwapBoxAvailableModes(operation, marketInfo); - const onOperationChange = useCallback( - (operation: Operation) => { - onSetOperation(operation); + useEffect( + function updateOperation() { + const isSelectedMarketShiftAvailable = Boolean( + shiftAvailableMarkets?.find((market) => market.marketTokenAddress === selectedMarketAddress) + ); + + const isShiftOperation = operation === Operation.Shift; + + if (isShiftOperation && !isSelectedMarketShiftAvailable) { + onSetOperation(Operation.Deposit); + } }, - [onSetOperation] + [onSetOperation, operation, selectedMarketAddress, shiftAvailableMarkets] ); const localizedOperationLabels = useLocalizedMap(OPERATION_LABELS); @@ -56,10 +83,10 @@ export function GmSwapBox(p: GmSwapBoxProps) { return (
diff --git a/src/components/Tab/Tab.css b/src/components/Tab/Tab.css index 3f9417cb12..f2e1835bf1 100644 --- a/src/components/Tab/Tab.css +++ b/src/components/Tab/Tab.css @@ -23,10 +23,9 @@ padding: 0.8rem; padding-left: 1.5rem; padding-right: 1.5rem; - cursor: pointer; } -.Tab.Tab__block .Tab-option:hover { +.Tab.Tab__block .Tab-option:not(.disabled):hover { color: var(--color-white); background: var(--color-cold-blue-700); } @@ -54,13 +53,12 @@ } .Tab.Tab__inline .Tab-option { - cursor: pointer; display: inline-block; margin-right: 1.5rem; font-size: var(--font-base); } -.Tab.Tab__inline .Tab-option:hover { +.Tab.Tab__inline .Tab-option:not(.disabled):hover { opacity: 0.8; } @@ -68,3 +66,11 @@ opacity: 1; pointer-events: none; } + +.Tab-option { + cursor: pointer; +} + +.Tab-option.disabled { + cursor: not-allowed; +} diff --git a/src/pages/MarketPoolsPage/MarketPoolsPage.tsx b/src/pages/MarketPoolsPage/MarketPoolsPage.tsx index b1a164fdc2..11e94528f3 100644 --- a/src/pages/MarketPoolsPage/MarketPoolsPage.tsx +++ b/src/pages/MarketPoolsPage/MarketPoolsPage.tsx @@ -3,6 +3,8 @@ import { useEffect, useRef, useState } from "react"; import { Mode, Operation } from "components/Synthetics/GmSwap/GmSwapBox/types"; import { getSyntheticsDepositMarketKey } from "config/localStorage"; +import { selectDepositMarketTokensData } from "context/SyntheticsStateContext/selectors/globalSelectors"; +import { useSelector } from "context/SyntheticsStateContext/utils"; import { MarketsInfoData, useMarketsInfoRequest, useMarketTokensData } from "domain/synthetics/markets"; import { useGmMarketsApy } from "domain/synthetics/markets/useGmMarketsApy"; import { getTokenData } from "domain/synthetics/tokens"; @@ -28,7 +30,7 @@ export function MarketPoolsPage() { const { marketsInfoData = EMPTY_OBJECT as MarketsInfoData } = useMarketsInfoRequest(chainId); - const { marketTokensData: depositMarketTokensData } = useMarketTokensData(chainId, { isDeposit: true }); + const depositMarketTokensData = useSelector(selectDepositMarketTokensData); const { marketTokensData: withdrawalMarketTokensData } = useMarketTokensData(chainId, { isDeposit: false }); const { marketsTokensApyData, marketsTokensIncentiveAprData } = useGmMarketsApy(chainId); From 507fc77d1404dabd40743316af7ec51dcb6d61f9 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 6 Aug 2024 18:43:42 +0000 Subject: [PATCH 36/62] Refactor createShiftTxn to import SetPendingShift type from context/SyntheticsEvents --- src/domain/synthetics/markets/createShiftTxn.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/domain/synthetics/markets/createShiftTxn.ts b/src/domain/synthetics/markets/createShiftTxn.ts index 3b9dee9e8e..858e07d75c 100644 --- a/src/domain/synthetics/markets/createShiftTxn.ts +++ b/src/domain/synthetics/markets/createShiftTxn.ts @@ -3,7 +3,7 @@ import { Signer, ethers } from "ethers"; import { getContract } from "config/contracts"; import { UI_FEE_RECEIVER_ACCOUNT } from "config/ui"; -import { SetPendingShift } from "context/SyntheticsEvents"; +import type { SetPendingShift } from "context/SyntheticsEvents"; import { callContract } from "lib/contracts"; import { simulateExecuteTxn } from "../orders/simulateExecuteTxn"; @@ -52,9 +52,7 @@ export async function createShiftTxn(chainId: number, signer: Signer, p: Params) }, ]; - const encodedPayload = multicall - .filter(Boolean) - .map((call) => contract.interface.encodeFunctionData(call!.method, call!.params)); + const encodedPayload = multicall.map((call) => contract.interface.encodeFunctionData(call!.method, call!.params)); if (!p.skipSimulation) { await simulateExecuteTxn(chainId, { From c3c3d78243a594a4f51f2a8be79bcfbb499f7449 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 6 Aug 2024 18:46:58 +0000 Subject: [PATCH 37/62] chore: Refactor useSortedPoolsWithIndexToken to import necessary types and remove unused imports --- .../synthetics/trade/useSortedPoolsWithIndexToken.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/domain/synthetics/trade/useSortedPoolsWithIndexToken.ts b/src/domain/synthetics/trade/useSortedPoolsWithIndexToken.ts index 2033dc51f0..cc248f83e1 100644 --- a/src/domain/synthetics/trade/useSortedPoolsWithIndexToken.ts +++ b/src/domain/synthetics/trade/useSortedPoolsWithIndexToken.ts @@ -1,8 +1,10 @@ -import { EMPTY_ARRAY, getByKey } from "lib/objects"; import groupBy from "lodash/groupBy"; import { useMemo } from "react"; -import { MarketInfo, MarketsInfoData } from "../markets"; -import { TokenData, TokensData, convertToUsd } from "../tokens"; + +import { EMPTY_ARRAY, getByKey } from "lib/objects"; + +import type { MarketInfo, MarketsInfoData } from "../markets"; +import { type TokenData, type TokensData, convertToUsd } from "../tokens"; const DEFAULT_VALUE = { markets: EMPTY_ARRAY, From 3ea31bd907030687d5f8f86599a481ea6a1b5a9f Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 6 Aug 2024 20:38:04 +0000 Subject: [PATCH 38/62] chore: Refactor deposit.ts to improve code readability and efficiency --- src/domain/synthetics/trade/utils/deposit.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/domain/synthetics/trade/utils/deposit.ts b/src/domain/synthetics/trade/utils/deposit.ts index dcc07513ad..431a32c0fc 100644 --- a/src/domain/synthetics/trade/utils/deposit.ts +++ b/src/domain/synthetics/trade/utils/deposit.ts @@ -127,15 +127,15 @@ export function getDepositAmounts(p: { const prevShortTokenUsd = convertToUsd(shortTokenAmount, shortToken.decimals, shortTokenPrice)!; const prevSumUsd = prevLongTokenUsd + prevShortTokenUsd; - const longPoolAmount = marketInfo.longPoolAmount; - const shortPoolAmount = marketInfo.shortPoolAmount; - - const longPoolUsd = convertToUsd(longPoolAmount, longToken.decimals, longToken.prices.maxPrice)!; - const shortPoolUsd = convertToUsd(shortPoolAmount, shortToken.decimals, shortToken.prices.maxPrice)!; - const totalPoolUsd = longPoolUsd + shortPoolUsd; - if (p.forShift) { // Reverse the withdrawal amounts + const longPoolAmount = marketInfo.longPoolAmount; + const shortPoolAmount = marketInfo.shortPoolAmount; + + const longPoolUsd = convertToUsd(longPoolAmount, longToken.decimals, longToken.prices.maxPrice)!; + const shortPoolUsd = convertToUsd(shortPoolAmount, shortToken.decimals, shortToken.prices.maxPrice)!; + const totalPoolUsd = longPoolUsd + shortPoolUsd; + values.longTokenUsd = bigMath.mulDiv(values.marketTokenUsd, longPoolUsd, totalPoolUsd); values.shortTokenUsd = bigMath.mulDiv(values.marketTokenUsd, shortPoolUsd, totalPoolUsd); } else if (includeLongToken && includeShortToken && prevSumUsd > 0) { From c36e1faaaef81b7ec7affd66f576f032351193a5 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Wed, 7 Aug 2024 10:23:25 +0000 Subject: [PATCH 39/62] Improve shift status notification with pool names --- .../StatusNotification/GmStatusNotification.tsx | 14 +++++++++++++- src/locales/de/messages.po | 8 ++++---- src/locales/en/messages.po | 8 ++++---- src/locales/es/messages.po | 8 ++++---- src/locales/fr/messages.po | 8 ++++---- src/locales/ja/messages.po | 8 ++++---- src/locales/ko/messages.po | 8 ++++---- src/locales/pseudo/messages.po | 8 ++++---- src/locales/ru/messages.po | 8 ++++---- src/locales/zh/messages.po | 8 ++++---- 10 files changed, 49 insertions(+), 37 deletions(-) diff --git a/src/components/Synthetics/StatusNotification/GmStatusNotification.tsx b/src/components/Synthetics/StatusNotification/GmStatusNotification.tsx index fa6a0494b9..8f93debad2 100644 --- a/src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +++ b/src/components/Synthetics/StatusNotification/GmStatusNotification.tsx @@ -188,12 +188,24 @@ export function GmStatusNotification({ const fromMarketInfo = getByKey(marketsInfoData, pendingShiftData.fromMarket); const fromIndexName = fromMarketInfo ? getMarketIndexName(fromMarketInfo) : ""; + const fromPoolName = fromMarketInfo ? getMarketPoolName(fromMarketInfo) : ""; + const toMarketInfo = getByKey(marketsInfoData, pendingShiftData.toMarket); const toIndexName = toMarketInfo ? getMarketIndexName(toMarketInfo) : ""; + const toPoolName = toMarketInfo ? getMarketPoolName(toMarketInfo) : ""; return ( - Shifting GM from {fromIndexName} to {toIndexName} + Shifting from{" "} + + GM: {fromIndexName} + [{fromPoolName}] + {" "} + to{" "} + + GM: {toIndexName} + [{toPoolName}] + ); } diff --git a/src/locales/de/messages.po b/src/locales/de/messages.po index a682d63551..af96244f2a 100644 --- a/src/locales/de/messages.po +++ b/src/locales/de/messages.po @@ -4819,6 +4819,10 @@ msgstr "" msgid "Insufficient liquidity to swap collateral" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "" + #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -7678,10 +7682,6 @@ msgstr "Genehmigung wurde aufgehoben" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from {fromIndexName} to {toIndexName}" -msgstr "" - #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "Erhöhen aktiv: {0}, ausgeführt: {1}, abgebrochen: {2}" diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index a6bc1eeff5..7958e5a253 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -4822,6 +4822,10 @@ msgstr "One-Click Trading is not available for wrapping or unwrapping native tok msgid "Insufficient liquidity to swap collateral" msgstr "Insufficient liquidity to swap collateral" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" + #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -7684,10 +7688,6 @@ msgstr "Approval was cancelled" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from {fromIndexName} to {toIndexName}" -msgstr "Shifting GM from {fromIndexName} to {toIndexName}" - #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "Increase active: {0}, executed: {1}, cancelled: {2}" diff --git a/src/locales/es/messages.po b/src/locales/es/messages.po index 3e46278e05..e4d19ee50d 100644 --- a/src/locales/es/messages.po +++ b/src/locales/es/messages.po @@ -4819,6 +4819,10 @@ msgstr "" msgid "Insufficient liquidity to swap collateral" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "" + #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -7678,10 +7682,6 @@ msgstr "Aprobación cancelada" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from {fromIndexName} to {toIndexName}" -msgstr "" - #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "Incrementar activo: {0}, ejecutado: {1}, cancelado: {2}" diff --git a/src/locales/fr/messages.po b/src/locales/fr/messages.po index 4bd28f3654..58f54d8141 100644 --- a/src/locales/fr/messages.po +++ b/src/locales/fr/messages.po @@ -4819,6 +4819,10 @@ msgstr "" msgid "Insufficient liquidity to swap collateral" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "" + #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -7678,10 +7682,6 @@ msgstr "L'approbation a été annulée" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from {fromIndexName} to {toIndexName}" -msgstr "" - #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "Augmentation active: {0}, exécuté: {1}, annulé: {2}" diff --git a/src/locales/ja/messages.po b/src/locales/ja/messages.po index b1e5398add..2e50b6f4c9 100644 --- a/src/locales/ja/messages.po +++ b/src/locales/ja/messages.po @@ -4819,6 +4819,10 @@ msgstr "" msgid "Insufficient liquidity to swap collateral" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "" + #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -7678,10 +7682,6 @@ msgstr "承認はキャンセルされました" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from {fromIndexName} to {toIndexName}" -msgstr "" - #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "増加 アクティブ: {0} 執行済: {1} キャンセル済: {2}" diff --git a/src/locales/ko/messages.po b/src/locales/ko/messages.po index c05254f32f..15379d8f04 100644 --- a/src/locales/ko/messages.po +++ b/src/locales/ko/messages.po @@ -4819,6 +4819,10 @@ msgstr "" msgid "Insufficient liquidity to swap collateral" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "" + #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -7678,10 +7682,6 @@ msgstr "제출 취소되었습니다" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from {fromIndexName} to {toIndexName}" -msgstr "" - #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "중가 액티브: {0}, 실행 완료: {1}, 취소 완료: {2}" diff --git a/src/locales/pseudo/messages.po b/src/locales/pseudo/messages.po index 7cfad90962..f0af7e49b0 100644 --- a/src/locales/pseudo/messages.po +++ b/src/locales/pseudo/messages.po @@ -4819,6 +4819,10 @@ msgstr "" msgid "Insufficient liquidity to swap collateral" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "" + #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -7678,10 +7682,6 @@ msgstr "" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from {fromIndexName} to {toIndexName}" -msgstr "" - #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "" diff --git a/src/locales/ru/messages.po b/src/locales/ru/messages.po index 73cecfa201..4b03762b72 100644 --- a/src/locales/ru/messages.po +++ b/src/locales/ru/messages.po @@ -4819,6 +4819,10 @@ msgstr "" msgid "Insufficient liquidity to swap collateral" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "" + #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -7678,10 +7682,6 @@ msgstr "Одобрение было отменено" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from {fromIndexName} to {toIndexName}" -msgstr "" - #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "Увеличение активно: {0}, выполнено: {1}, отменено: {2}" diff --git a/src/locales/zh/messages.po b/src/locales/zh/messages.po index 13cdb7889f..dc7e76681d 100644 --- a/src/locales/zh/messages.po +++ b/src/locales/zh/messages.po @@ -4819,6 +4819,10 @@ msgstr "" msgid "Insufficient liquidity to swap collateral" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "" + #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -7678,10 +7682,6 @@ msgstr "核准已被取消" msgid "{longOrShort} positions do not pay a funding fee and pay a borrow fee of {borrowRate} per hour." msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from {fromIndexName} to {toIndexName}" -msgstr "" - #: src/pages/OrdersOverview/OrdersOverview.js msgid "Increase active: {0}, executed: {1}, cancelled: {2}" msgstr "提升活跃度:{0},执行。{1},取消了。{2}" From 1dea26a7548cd48fb8a3bda47b2e315570ad3d87 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Wed, 7 Aug 2024 10:46:08 +0000 Subject: [PATCH 40/62] Hide shift button on gm list --- src/components/Synthetics/GmList/GmList.tsx | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/components/Synthetics/GmList/GmList.tsx b/src/components/Synthetics/GmList/GmList.tsx index 7af8647a32..1eb6d08cb0 100644 --- a/src/components/Synthetics/GmList/GmList.tsx +++ b/src/components/Synthetics/GmList/GmList.tsx @@ -1,4 +1,5 @@ import { Trans, t } from "@lingui/macro"; +import cx from "classnames"; import noop from "lodash/noop"; import { useCallback, useMemo, useState } from "react"; import { Address, isAddress, isAddressEqual } from "viem"; @@ -437,14 +438,19 @@ function GmListItem({ > Sell - + +
From 1f164296dc75d6d5a20f025076976e97421aae86 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Wed, 7 Aug 2024 10:46:44 +0000 Subject: [PATCH 41/62] Update shift request texts for better clarity and consistency --- .../GmStatusNotification.tsx | 12 ++-- src/locales/de/messages.po | 56 +++++++++---------- src/locales/en/messages.po | 56 +++++++++---------- src/locales/es/messages.po | 56 +++++++++---------- src/locales/fr/messages.po | 56 +++++++++---------- src/locales/ja/messages.po | 56 +++++++++---------- src/locales/ko/messages.po | 56 +++++++++---------- src/locales/pseudo/messages.po | 56 +++++++++---------- src/locales/ru/messages.po | 56 +++++++++---------- src/locales/zh/messages.po | 56 +++++++++---------- 10 files changed, 258 insertions(+), 258 deletions(-) diff --git a/src/components/Synthetics/StatusNotification/GmStatusNotification.tsx b/src/components/Synthetics/StatusNotification/GmStatusNotification.tsx index 8f93debad2..3e264139ac 100644 --- a/src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +++ b/src/components/Synthetics/StatusNotification/GmStatusNotification.tsx @@ -217,7 +217,7 @@ export function GmStatusNotification({ let createdTxnHash: string | undefined; if (operation === "deposit") { - text = t`Sending Buy request`; + text = t`Sending buy request`; if (depositStatus?.createdTxnHash) { text = t`Buy request sent`; @@ -225,7 +225,7 @@ export function GmStatusNotification({ createdTxnHash = depositStatus?.createdTxnHash; } } else if (operation === "withdrawal") { - text = t`Sending Sell request`; + text = t`Sending sell request`; if (withdrawalStatus?.createdTxnHash) { text = t`Sell request sent`; @@ -233,7 +233,7 @@ export function GmStatusNotification({ createdTxnHash = withdrawalStatus?.createdTxnHash; } } else { - text = t`Sending Shift request`; + text = t`Sending shift request`; if (shiftStatus?.createdTxnHash) { text = t`Shift request sent`; @@ -251,7 +251,7 @@ export function GmStatusNotification({ let txnHash: string | undefined; if (operation === "deposit") { - text = t`Fulfilling Buy request`; + text = t`Fulfilling buy request`; if (depositStatus?.createdTxnHash) { status = "loading"; @@ -269,7 +269,7 @@ export function GmStatusNotification({ txnHash = depositStatus?.cancelledTxnHash; } } else if (operation === "withdrawal") { - text = t`Fulfilling Sell request`; + text = t`Fulfilling sell request`; if (withdrawalStatus?.createdTxnHash) { status = "loading"; @@ -287,7 +287,7 @@ export function GmStatusNotification({ txnHash = withdrawalStatus?.cancelledTxnHash; } } else { - text = t`Fulfilling Shift request`; + text = t`Fulfilling shift request`; if (shiftStatus?.createdTxnHash) { status = "loading"; diff --git a/src/locales/de/messages.po b/src/locales/de/messages.po index af96244f2a..3ea3974fcd 100644 --- a/src/locales/de/messages.po +++ b/src/locales/de/messages.po @@ -627,6 +627,10 @@ msgstr "" msgid "Market Orders" msgstr "Markt-Orders" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending buy request" +msgstr "" + #: src/pages/ClaimEsGmx/ClaimEsGmx.js msgid "You can check your claim history <0>here." msgstr "Du kannst deine Claim-Historie <0>hier einsehen." @@ -898,10 +902,6 @@ msgstr "Min. Empfangen" msgid "Estimated Fee Refund" msgstr "Geschätzte Gebührenerstattung" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Shift request" -msgstr "" - #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "" @@ -1248,10 +1248,6 @@ msgstr "Swap Order Erstellung fehlgeschlagen." msgid "Chart positions" msgstr "Chart-Positionen" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Shift request" -msgstr "" - #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "Der Auftrag wird nur ausgeführt, wenn die Preisbedingungen erfüllt sind und genügend Liquidität vorhanden ist." @@ -2177,6 +2173,10 @@ msgstr "{0} USD in {tokenSymbol} {longOrShortText} eingezahlt" msgid "Receiver has staked GMX/GLP before" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling sell request" +msgstr "" + #: src/components/Exchange/TradeHistory.js msgid "{actionDisplay} Order" msgstr "{actionDisplay} Order" @@ -3501,6 +3501,10 @@ msgstr "Netto-Rate / 1h" msgid "Claim esGMX Rewards" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending sell request" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.js msgid "Structured Products" msgstr "Strukturierte Produkte" @@ -3895,10 +3899,6 @@ msgstr "Einen Code eingeben" msgid "Execute Take-Profit Order" msgstr "Take-Profit Order ausführen" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Buy request" -msgstr "" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Subaccount is updated" msgstr "" @@ -4199,6 +4199,10 @@ msgstr "Der Ausführungspreis erfüllt die akzeptable Preisbedingung nicht." msgid "Reserve Amount" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling buy request" +msgstr "" + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Reason: {0}" msgstr "Grund: {0}" @@ -4282,10 +4286,6 @@ msgstr "" msgid "Swap submitted!" msgstr "Swap übermittelt!" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Buy request" -msgstr "" - #: src/pages/Ecosystem/Ecosystem.js msgid "GBC NFTs APR tracker and rewards" msgstr "GBC NFTs APR-Tracker und Belohnungen" @@ -4547,6 +4547,10 @@ msgstr "Preis" msgid "Realized PnL insufficient for Fees" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending shift request" +msgstr "" + #: src/components/Exchange/SwapBox.js #: src/pages/Stake/StakeV1.js msgid "Incorrect Network" @@ -4819,10 +4823,6 @@ msgstr "" msgid "Insufficient liquidity to swap collateral" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" -msgstr "" - #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -5780,6 +5780,10 @@ msgstr "Gevestete GMX nicht abgehoben" msgid "FEES" msgstr "GEBÜHREN" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "" + #: src/components/Synthetics/GmList/GmList.tsx msgid "TOTAL SUPPLY" msgstr "" @@ -6101,10 +6105,6 @@ msgstr "" msgid "Buy GMX using FIAT gateways:" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Sell request" -msgstr "" - #: src/domain/synthetics/orders/createDecreaseOrderTxn.ts #: src/domain/synthetics/orders/createIncreaseOrderTxn.ts #: src/domain/synthetics/orders/createSwapOrderTxn.ts @@ -6420,10 +6420,6 @@ msgstr "" msgid "View on Arbitrum" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Sell request" -msgstr "" - #: src/domain/synthetics/markets/createShiftTxn.ts msgid "Shift error." msgstr "" @@ -6801,6 +6797,10 @@ msgstr "Markt Swap fehlgeschlagen" msgid "Only letters, numbers and underscores are allowed." msgstr "Es sind nur Buchstaben, Zahlen und Unterstriche erlaubt." +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling shift request" +msgstr "" + #: src/pages/Referrals/Referrals.tsx msgid "Referral code submitted!" msgstr "" diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index 7958e5a253..f7b1546fd1 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -627,6 +627,10 @@ msgstr "Bid Price (Exit)" msgid "Market Orders" msgstr "Market Orders" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending buy request" +msgstr "Sending buy request" + #: src/pages/ClaimEsGmx/ClaimEsGmx.js msgid "You can check your claim history <0>here." msgstr "You can check your claim history <0>here." @@ -898,10 +902,6 @@ msgstr "Min. Receive" msgid "Estimated Fee Refund" msgstr "Estimated Fee Refund" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Shift request" -msgstr "Fulfilling Shift request" - #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "Deactivating..." @@ -1248,10 +1248,6 @@ msgstr "Swap Order creation failed." msgid "Chart positions" msgstr "Chart positions" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Shift request" -msgstr "Sending Shift request" - #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "The order will only execute if the price conditions are met and there is sufficient liquidity" @@ -2177,6 +2173,10 @@ msgstr "Deposited {0} USD into {tokenSymbol} {longOrShortText}" msgid "Receiver has staked GMX/GLP before" msgstr "Receiver has staked GMX/GLP before" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling sell request" +msgstr "Fulfilling sell request" + #: src/components/Exchange/TradeHistory.js msgid "{actionDisplay} Order" msgstr "{actionDisplay} Order" @@ -3504,6 +3504,10 @@ msgstr "Net Rate / 1h" msgid "Claim esGMX Rewards" msgstr "Claim esGMX Rewards" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending sell request" +msgstr "Sending sell request" + #: src/pages/Ecosystem/Ecosystem.js msgid "Structured Products" msgstr "Structured Products" @@ -3898,10 +3902,6 @@ msgstr "Enter a code" msgid "Execute Take-Profit Order" msgstr "Execute Take-Profit Order" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Buy request" -msgstr "Sending Buy request" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Subaccount is updated" msgstr "Subaccount is updated" @@ -4202,6 +4202,10 @@ msgstr "The Execution Price didn't meet the Acceptable Price condition." msgid "Reserve Amount" msgstr "Reserve Amount" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling buy request" +msgstr "Fulfilling buy request" + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Reason: {0}" msgstr "Reason: {0}" @@ -4285,10 +4289,6 @@ msgstr "<0>The order will be executed when the oracle price is {0} {1}.<1/>< msgid "Swap submitted!" msgstr "Swap submitted!" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Buy request" -msgstr "Fulfilling Buy request" - #: src/pages/Ecosystem/Ecosystem.js msgid "GBC NFTs APR tracker and rewards" msgstr "GBC NFTs APR tracker and rewards" @@ -4550,6 +4550,10 @@ msgstr "Price" msgid "Realized PnL insufficient for Fees" msgstr "Realized PnL insufficient for Fees" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending shift request" +msgstr "Sending shift request" + #: src/components/Exchange/SwapBox.js #: src/pages/Stake/StakeV1.js msgid "Incorrect Network" @@ -4822,10 +4826,6 @@ msgstr "One-Click Trading is not available for wrapping or unwrapping native tok msgid "Insufficient liquidity to swap collateral" msgstr "Insufficient liquidity to swap collateral" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" -msgstr "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" - #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -5786,6 +5786,10 @@ msgstr "Vested GMX not withdrawn" msgid "FEES" msgstr "FEES" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "Shifting from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" + #: src/components/Synthetics/GmList/GmList.tsx msgid "TOTAL SUPPLY" msgstr "TOTAL SUPPLY" @@ -6107,10 +6111,6 @@ msgstr "Price Impact not yet acknowledged" msgid "Buy GMX using FIAT gateways:" msgstr "Buy GMX using FIAT gateways:" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Sell request" -msgstr "Sending Sell request" - #: src/domain/synthetics/orders/createDecreaseOrderTxn.ts #: src/domain/synthetics/orders/createIncreaseOrderTxn.ts #: src/domain/synthetics/orders/createSwapOrderTxn.ts @@ -6426,10 +6426,6 @@ msgstr "Sell GM" msgid "View on Arbitrum" msgstr "View on Arbitrum" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Sell request" -msgstr "Fulfilling Sell request" - #: src/domain/synthetics/markets/createShiftTxn.ts msgid "Shift error." msgstr "Shift error." @@ -6807,6 +6803,10 @@ msgstr "Failed Market Swap" msgid "Only letters, numbers and underscores are allowed." msgstr "Only letters, numbers and underscores are allowed." +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling shift request" +msgstr "Fulfilling shift request" + #: src/pages/Referrals/Referrals.tsx msgid "Referral code submitted!" msgstr "Referral code submitted!" diff --git a/src/locales/es/messages.po b/src/locales/es/messages.po index e4d19ee50d..8b2b7b9807 100644 --- a/src/locales/es/messages.po +++ b/src/locales/es/messages.po @@ -627,6 +627,10 @@ msgstr "" msgid "Market Orders" msgstr "Órdenes de Mercado" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending buy request" +msgstr "" + #: src/pages/ClaimEsGmx/ClaimEsGmx.js msgid "You can check your claim history <0>here." msgstr "Puede consultar su historial de reclamaciones <0>aquí." @@ -898,10 +902,6 @@ msgstr "Min. a Recibir" msgid "Estimated Fee Refund" msgstr "Reembolso de Comisión Estimado" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Shift request" -msgstr "" - #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "" @@ -1248,10 +1248,6 @@ msgstr "Falló la creación de la Orden de Intercambio." msgid "Chart positions" msgstr "Posiciones en gráfca" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Shift request" -msgstr "" - #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "La orden sólo se ejecutará si las condiciones de precio se alcanzan y hay suficiente liquidez" @@ -2177,6 +2173,10 @@ msgstr "Depositado {0} USD en {tokenSymbol} {longOrShortText}" msgid "Receiver has staked GMX/GLP before" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling sell request" +msgstr "" + #: src/components/Exchange/TradeHistory.js msgid "{actionDisplay} Order" msgstr "{actionDisplay} Orden" @@ -3501,6 +3501,10 @@ msgstr "Tasa Neta / 1h" msgid "Claim esGMX Rewards" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending sell request" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.js msgid "Structured Products" msgstr "Productos Estructurados" @@ -3895,10 +3899,6 @@ msgstr "Introduzca un código" msgid "Execute Take-Profit Order" msgstr "Ejecutar Orden de Take-Profit" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Buy request" -msgstr "" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Subaccount is updated" msgstr "" @@ -4199,6 +4199,10 @@ msgstr "El precio de ejecución no cumplió la condición de precio aceptable." msgid "Reserve Amount" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling buy request" +msgstr "" + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Reason: {0}" msgstr "Razón: {0}" @@ -4282,10 +4286,6 @@ msgstr "" msgid "Swap submitted!" msgstr "¡Intercambio Enviado!" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Buy request" -msgstr "" - #: src/pages/Ecosystem/Ecosystem.js msgid "GBC NFTs APR tracker and rewards" msgstr "Rastreador de GBC NFTs APR y recompensas" @@ -4547,6 +4547,10 @@ msgstr "Precio" msgid "Realized PnL insufficient for Fees" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending shift request" +msgstr "" + #: src/components/Exchange/SwapBox.js #: src/pages/Stake/StakeV1.js msgid "Incorrect Network" @@ -4819,10 +4823,6 @@ msgstr "" msgid "Insufficient liquidity to swap collateral" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" -msgstr "" - #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -5780,6 +5780,10 @@ msgstr "GMX adquirido no retirado" msgid "FEES" msgstr "COMISIONES" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "" + #: src/components/Synthetics/GmList/GmList.tsx msgid "TOTAL SUPPLY" msgstr "" @@ -6101,10 +6105,6 @@ msgstr "" msgid "Buy GMX using FIAT gateways:" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Sell request" -msgstr "" - #: src/domain/synthetics/orders/createDecreaseOrderTxn.ts #: src/domain/synthetics/orders/createIncreaseOrderTxn.ts #: src/domain/synthetics/orders/createSwapOrderTxn.ts @@ -6420,10 +6420,6 @@ msgstr "" msgid "View on Arbitrum" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Sell request" -msgstr "" - #: src/domain/synthetics/markets/createShiftTxn.ts msgid "Shift error." msgstr "" @@ -6801,6 +6797,10 @@ msgstr "Intercambio de Mercado Fallido" msgid "Only letters, numbers and underscores are allowed." msgstr "Solo letras, números y guiones bajos están permitidos" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling shift request" +msgstr "" + #: src/pages/Referrals/Referrals.tsx msgid "Referral code submitted!" msgstr "" diff --git a/src/locales/fr/messages.po b/src/locales/fr/messages.po index 58f54d8141..d545f102c3 100644 --- a/src/locales/fr/messages.po +++ b/src/locales/fr/messages.po @@ -627,6 +627,10 @@ msgstr "" msgid "Market Orders" msgstr "Ordres de marché" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending buy request" +msgstr "" + #: src/pages/ClaimEsGmx/ClaimEsGmx.js msgid "You can check your claim history <0>here." msgstr "Vous pouvez vérifier l'historique de vos réclamations <0>ici." @@ -898,10 +902,6 @@ msgstr "Reçu Min." msgid "Estimated Fee Refund" msgstr "Remboursement estimé des frais" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Shift request" -msgstr "" - #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "" @@ -1248,10 +1248,6 @@ msgstr "Ordre d'échange échoué." msgid "Chart positions" msgstr "Positions sur les graphiques" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Shift request" -msgstr "" - #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "L'ordre ne sera exécuté que si les conditions de prix sont remplies et si la liquidité est suffisante." @@ -2177,6 +2173,10 @@ msgstr "{0} USD déposés sur {tokenSymbol} {longOrShortText}" msgid "Receiver has staked GMX/GLP before" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling sell request" +msgstr "" + #: src/components/Exchange/TradeHistory.js msgid "{actionDisplay} Order" msgstr "{actionDisplay} Ordre" @@ -3501,6 +3501,10 @@ msgstr "Taux net / 1h" msgid "Claim esGMX Rewards" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending sell request" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.js msgid "Structured Products" msgstr "Produits structurés" @@ -3895,10 +3899,6 @@ msgstr "Indiquer un code" msgid "Execute Take-Profit Order" msgstr "Exécuter l'ordre de prise de profit" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Buy request" -msgstr "" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Subaccount is updated" msgstr "" @@ -4199,6 +4199,10 @@ msgstr "Le prix d'exécution n'a pas respecté la condition de prix acceptable." msgid "Reserve Amount" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling buy request" +msgstr "" + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Reason: {0}" msgstr "Raison: {0}" @@ -4282,10 +4286,6 @@ msgstr "" msgid "Swap submitted!" msgstr "Échange soumis !" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Buy request" -msgstr "" - #: src/pages/Ecosystem/Ecosystem.js msgid "GBC NFTs APR tracker and rewards" msgstr "Tracker de taux de rendement annuels des NFTs GBC et récompenses" @@ -4547,6 +4547,10 @@ msgstr "Prix" msgid "Realized PnL insufficient for Fees" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending shift request" +msgstr "" + #: src/components/Exchange/SwapBox.js #: src/pages/Stake/StakeV1.js msgid "Incorrect Network" @@ -4819,10 +4823,6 @@ msgstr "" msgid "Insufficient liquidity to swap collateral" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" -msgstr "" - #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -5780,6 +5780,10 @@ msgstr "GMX vesté non retiré" msgid "FEES" msgstr "FRAIS" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "" + #: src/components/Synthetics/GmList/GmList.tsx msgid "TOTAL SUPPLY" msgstr "" @@ -6101,10 +6105,6 @@ msgstr "" msgid "Buy GMX using FIAT gateways:" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Sell request" -msgstr "" - #: src/domain/synthetics/orders/createDecreaseOrderTxn.ts #: src/domain/synthetics/orders/createIncreaseOrderTxn.ts #: src/domain/synthetics/orders/createSwapOrderTxn.ts @@ -6420,10 +6420,6 @@ msgstr "" msgid "View on Arbitrum" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Sell request" -msgstr "" - #: src/domain/synthetics/markets/createShiftTxn.ts msgid "Shift error." msgstr "" @@ -6801,6 +6797,10 @@ msgstr "Échange de marché échoué" msgid "Only letters, numbers and underscores are allowed." msgstr "Les lettres, les chiffres et les caractères de soulignement sont autorisés." +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling shift request" +msgstr "" + #: src/pages/Referrals/Referrals.tsx msgid "Referral code submitted!" msgstr "" diff --git a/src/locales/ja/messages.po b/src/locales/ja/messages.po index 2e50b6f4c9..dea61ab415 100644 --- a/src/locales/ja/messages.po +++ b/src/locales/ja/messages.po @@ -627,6 +627,10 @@ msgstr "" msgid "Market Orders" msgstr "市場注文" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending buy request" +msgstr "" + #: src/pages/ClaimEsGmx/ClaimEsGmx.js msgid "You can check your claim history <0>here." msgstr "<0>こちらで請求履歴を確認できます。" @@ -898,10 +902,6 @@ msgstr "最低受け取り額" msgid "Estimated Fee Refund" msgstr "見積もり手数料返金" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Shift request" -msgstr "" - #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "" @@ -1248,10 +1248,6 @@ msgstr "スワップ注文が作成できませんでした。" msgid "Chart positions" msgstr "ポジションをチャートに" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Shift request" -msgstr "" - #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "価格の条件が満たされ、さらに十分な流動性が存在している場合にのみ注文は執行されます。" @@ -2177,6 +2173,10 @@ msgstr "{0} USDを {tokenSymbol} {longOrShortText}に入金済" msgid "Receiver has staked GMX/GLP before" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling sell request" +msgstr "" + #: src/components/Exchange/TradeHistory.js msgid "{actionDisplay} Order" msgstr "{actionDisplay} 注文" @@ -3501,6 +3501,10 @@ msgstr "ネットレート / 1時間" msgid "Claim esGMX Rewards" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending sell request" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.js msgid "Structured Products" msgstr "ストラクチャードプロダクト" @@ -3895,10 +3899,6 @@ msgstr "コードを入力" msgid "Execute Take-Profit Order" msgstr "テイクプロフィット注文の執行" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Buy request" -msgstr "" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Subaccount is updated" msgstr "" @@ -4199,6 +4199,10 @@ msgstr "実行価格が許容価格条件を満たしませんでした。" msgid "Reserve Amount" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling buy request" +msgstr "" + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Reason: {0}" msgstr "理由: {0}" @@ -4282,10 +4286,6 @@ msgstr "" msgid "Swap submitted!" msgstr "スワップ申し込み完了!" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Buy request" -msgstr "" - #: src/pages/Ecosystem/Ecosystem.js msgid "GBC NFTs APR tracker and rewards" msgstr "GBC NFTのAPRトラッカーおよび報酬" @@ -4547,6 +4547,10 @@ msgstr "価格" msgid "Realized PnL insufficient for Fees" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending shift request" +msgstr "" + #: src/components/Exchange/SwapBox.js #: src/pages/Stake/StakeV1.js msgid "Incorrect Network" @@ -4819,10 +4823,6 @@ msgstr "" msgid "Insufficient liquidity to swap collateral" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" -msgstr "" - #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -5780,6 +5780,10 @@ msgstr "べスティングされたGMXで引き出されていないもの" msgid "FEES" msgstr "手数料" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "" + #: src/components/Synthetics/GmList/GmList.tsx msgid "TOTAL SUPPLY" msgstr "" @@ -6101,10 +6105,6 @@ msgstr "" msgid "Buy GMX using FIAT gateways:" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Sell request" -msgstr "" - #: src/domain/synthetics/orders/createDecreaseOrderTxn.ts #: src/domain/synthetics/orders/createIncreaseOrderTxn.ts #: src/domain/synthetics/orders/createSwapOrderTxn.ts @@ -6420,10 +6420,6 @@ msgstr "" msgid "View on Arbitrum" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Sell request" -msgstr "" - #: src/domain/synthetics/markets/createShiftTxn.ts msgid "Shift error." msgstr "" @@ -6801,6 +6797,10 @@ msgstr "マーケットスワップ失敗" msgid "Only letters, numbers and underscores are allowed." msgstr "英数字とアンダースコア記号のみ使用できます。" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling shift request" +msgstr "" + #: src/pages/Referrals/Referrals.tsx msgid "Referral code submitted!" msgstr "" diff --git a/src/locales/ko/messages.po b/src/locales/ko/messages.po index 15379d8f04..50a847b269 100644 --- a/src/locales/ko/messages.po +++ b/src/locales/ko/messages.po @@ -627,6 +627,10 @@ msgstr "" msgid "Market Orders" msgstr "시장 주문" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending buy request" +msgstr "" + #: src/pages/ClaimEsGmx/ClaimEsGmx.js msgid "You can check your claim history <0>here." msgstr "<0>여기에서 수령한 내역을 확인할 수 있습니다." @@ -898,10 +902,6 @@ msgstr "최소 수취량" msgid "Estimated Fee Refund" msgstr "예상 수수료 환불" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Shift request" -msgstr "" - #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "" @@ -1248,10 +1248,6 @@ msgstr "스왑 주문 생성 실패." msgid "Chart positions" msgstr "차트 포지션" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Shift request" -msgstr "" - #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "주문은 가격 조건 및 유동성 충분 요건이 만족하는 경우에만 실행됩니다" @@ -2177,6 +2173,10 @@ msgstr "{0} USD를 {tokenSymbol} {longOrShortText}으로 입금 완료" msgid "Receiver has staked GMX/GLP before" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling sell request" +msgstr "" + #: src/components/Exchange/TradeHistory.js msgid "{actionDisplay} Order" msgstr "{actionDisplay} 주문" @@ -3501,6 +3501,10 @@ msgstr "1시간 당 순 수수료율" msgid "Claim esGMX Rewards" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending sell request" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.js msgid "Structured Products" msgstr "구조화금융 프로덕트" @@ -3895,10 +3899,6 @@ msgstr "코드 입력" msgid "Execute Take-Profit Order" msgstr "이익 실현 주문 실행" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Buy request" -msgstr "" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Subaccount is updated" msgstr "" @@ -4199,6 +4199,10 @@ msgstr "실행 가격이 허용 가능한 가격 조건을 충족하지 않았 msgid "Reserve Amount" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling buy request" +msgstr "" + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Reason: {0}" msgstr "이유: {0}" @@ -4282,10 +4286,6 @@ msgstr "" msgid "Swap submitted!" msgstr "스왑 제출완료" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Buy request" -msgstr "" - #: src/pages/Ecosystem/Ecosystem.js msgid "GBC NFTs APR tracker and rewards" msgstr "GBC NFT의 APR 트래커 및 보상" @@ -4547,6 +4547,10 @@ msgstr "가격" msgid "Realized PnL insufficient for Fees" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending shift request" +msgstr "" + #: src/components/Exchange/SwapBox.js #: src/pages/Stake/StakeV1.js msgid "Incorrect Network" @@ -4819,10 +4823,6 @@ msgstr "" msgid "Insufficient liquidity to swap collateral" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" -msgstr "" - #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -5780,6 +5780,10 @@ msgstr "베스트팅된 GMX에서 인출되지 않은 양" msgid "FEES" msgstr "수수료" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "" + #: src/components/Synthetics/GmList/GmList.tsx msgid "TOTAL SUPPLY" msgstr "" @@ -6101,10 +6105,6 @@ msgstr "" msgid "Buy GMX using FIAT gateways:" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Sell request" -msgstr "" - #: src/domain/synthetics/orders/createDecreaseOrderTxn.ts #: src/domain/synthetics/orders/createIncreaseOrderTxn.ts #: src/domain/synthetics/orders/createSwapOrderTxn.ts @@ -6420,10 +6420,6 @@ msgstr "" msgid "View on Arbitrum" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Sell request" -msgstr "" - #: src/domain/synthetics/markets/createShiftTxn.ts msgid "Shift error." msgstr "" @@ -6801,6 +6797,10 @@ msgstr "시장 스왑 실패" msgid "Only letters, numbers and underscores are allowed." msgstr "문자, 숫자 및 언더스코어만 가능합니다" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling shift request" +msgstr "" + #: src/pages/Referrals/Referrals.tsx msgid "Referral code submitted!" msgstr "" diff --git a/src/locales/pseudo/messages.po b/src/locales/pseudo/messages.po index f0af7e49b0..ce020d3060 100644 --- a/src/locales/pseudo/messages.po +++ b/src/locales/pseudo/messages.po @@ -627,6 +627,10 @@ msgstr "" msgid "Market Orders" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending buy request" +msgstr "" + #: src/pages/ClaimEsGmx/ClaimEsGmx.js msgid "You can check your claim history <0>here." msgstr "" @@ -898,10 +902,6 @@ msgstr "" msgid "Estimated Fee Refund" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Shift request" -msgstr "" - #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "" @@ -1248,10 +1248,6 @@ msgstr "" msgid "Chart positions" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Shift request" -msgstr "" - #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "" @@ -2177,6 +2173,10 @@ msgstr "" msgid "Receiver has staked GMX/GLP before" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling sell request" +msgstr "" + #: src/components/Exchange/TradeHistory.js msgid "{actionDisplay} Order" msgstr "" @@ -3501,6 +3501,10 @@ msgstr "" msgid "Claim esGMX Rewards" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending sell request" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.js msgid "Structured Products" msgstr "" @@ -3895,10 +3899,6 @@ msgstr "" msgid "Execute Take-Profit Order" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Buy request" -msgstr "" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Subaccount is updated" msgstr "" @@ -4199,6 +4199,10 @@ msgstr "" msgid "Reserve Amount" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling buy request" +msgstr "" + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Reason: {0}" msgstr "" @@ -4282,10 +4286,6 @@ msgstr "" msgid "Swap submitted!" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Buy request" -msgstr "" - #: src/pages/Ecosystem/Ecosystem.js msgid "GBC NFTs APR tracker and rewards" msgstr "" @@ -4547,6 +4547,10 @@ msgstr "" msgid "Realized PnL insufficient for Fees" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending shift request" +msgstr "" + #: src/components/Exchange/SwapBox.js #: src/pages/Stake/StakeV1.js msgid "Incorrect Network" @@ -4819,10 +4823,6 @@ msgstr "" msgid "Insufficient liquidity to swap collateral" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" -msgstr "" - #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -5780,6 +5780,10 @@ msgstr "" msgid "FEES" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "" + #: src/components/Synthetics/GmList/GmList.tsx msgid "TOTAL SUPPLY" msgstr "" @@ -6101,10 +6105,6 @@ msgstr "" msgid "Buy GMX using FIAT gateways:" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Sell request" -msgstr "" - #: src/domain/synthetics/orders/createDecreaseOrderTxn.ts #: src/domain/synthetics/orders/createIncreaseOrderTxn.ts #: src/domain/synthetics/orders/createSwapOrderTxn.ts @@ -6420,10 +6420,6 @@ msgstr "" msgid "View on Arbitrum" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Sell request" -msgstr "" - #: src/domain/synthetics/markets/createShiftTxn.ts msgid "Shift error." msgstr "" @@ -6801,6 +6797,10 @@ msgstr "" msgid "Only letters, numbers and underscores are allowed." msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling shift request" +msgstr "" + #: src/pages/Referrals/Referrals.tsx msgid "Referral code submitted!" msgstr "" diff --git a/src/locales/ru/messages.po b/src/locales/ru/messages.po index 4b03762b72..da8acf5c71 100644 --- a/src/locales/ru/messages.po +++ b/src/locales/ru/messages.po @@ -627,6 +627,10 @@ msgstr "" msgid "Market Orders" msgstr "Рыночные Ордера" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending buy request" +msgstr "" + #: src/pages/ClaimEsGmx/ClaimEsGmx.js msgid "You can check your claim history <0>here." msgstr "Вы можете проверить историю своих обращений <0>здесь." @@ -898,10 +902,6 @@ msgstr "Мин. Приём" msgid "Estimated Fee Refund" msgstr "Прогнозируемый Возврат Сбора" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Shift request" -msgstr "" - #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "" @@ -1248,10 +1248,6 @@ msgstr "Обменный Ордер не создан" msgid "Chart positions" msgstr "Позиции на графике" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Shift request" -msgstr "" - #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "" @@ -2177,6 +2173,10 @@ msgstr "Внесено {0} USD на {tokenSymbol} {longOrShortText}" msgid "Receiver has staked GMX/GLP before" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling sell request" +msgstr "" + #: src/components/Exchange/TradeHistory.js msgid "{actionDisplay} Order" msgstr "{actionDisplay} Ордер" @@ -3501,6 +3501,10 @@ msgstr "Чистая ставка / 1ч" msgid "Claim esGMX Rewards" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending sell request" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.js msgid "Structured Products" msgstr "Структурированные продукты" @@ -3895,10 +3899,6 @@ msgstr "Введите код" msgid "Execute Take-Profit Order" msgstr "Исполнение тейк-профит ордера" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Buy request" -msgstr "" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Subaccount is updated" msgstr "" @@ -4199,6 +4199,10 @@ msgstr "Цена исполнения не соответствует услов msgid "Reserve Amount" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling buy request" +msgstr "" + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Reason: {0}" msgstr "Причина: {0}" @@ -4282,10 +4286,6 @@ msgstr "" msgid "Swap submitted!" msgstr "Обмен подан!" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Buy request" -msgstr "" - #: src/pages/Ecosystem/Ecosystem.js msgid "GBC NFTs APR tracker and rewards" msgstr "GBC NFTs APR отслеживание и вознаграждения" @@ -4547,6 +4547,10 @@ msgstr "Цена" msgid "Realized PnL insufficient for Fees" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending shift request" +msgstr "" + #: src/components/Exchange/SwapBox.js #: src/pages/Stake/StakeV1.js msgid "Incorrect Network" @@ -4819,10 +4823,6 @@ msgstr "" msgid "Insufficient liquidity to swap collateral" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" -msgstr "" - #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -5780,6 +5780,10 @@ msgstr "Вестинг GMX не выведен" msgid "FEES" msgstr "СБОРЫ" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "" + #: src/components/Synthetics/GmList/GmList.tsx msgid "TOTAL SUPPLY" msgstr "" @@ -6101,10 +6105,6 @@ msgstr "" msgid "Buy GMX using FIAT gateways:" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Sell request" -msgstr "" - #: src/domain/synthetics/orders/createDecreaseOrderTxn.ts #: src/domain/synthetics/orders/createIncreaseOrderTxn.ts #: src/domain/synthetics/orders/createSwapOrderTxn.ts @@ -6420,10 +6420,6 @@ msgstr "Продать GM" msgid "View on Arbitrum" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Sell request" -msgstr "" - #: src/domain/synthetics/markets/createShiftTxn.ts msgid "Shift error." msgstr "" @@ -6801,6 +6797,10 @@ msgstr "Рыночный обмен не удался" msgid "Only letters, numbers and underscores are allowed." msgstr "Допускаются только буквы, цифры и знаки подчеркивания." +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling shift request" +msgstr "" + #: src/pages/Referrals/Referrals.tsx msgid "Referral code submitted!" msgstr "" diff --git a/src/locales/zh/messages.po b/src/locales/zh/messages.po index dc7e76681d..1266f44fd4 100644 --- a/src/locales/zh/messages.po +++ b/src/locales/zh/messages.po @@ -627,6 +627,10 @@ msgstr "" msgid "Market Orders" msgstr "市场订单" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending buy request" +msgstr "" + #: src/pages/ClaimEsGmx/ClaimEsGmx.js msgid "You can check your claim history <0>here." msgstr "你可以在这里查看你的提领历史<0>" @@ -898,10 +902,6 @@ msgstr "最低接收" msgid "Estimated Fee Refund" msgstr "估计的费用退款" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Shift request" -msgstr "" - #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "Deactivating..." msgstr "" @@ -1248,10 +1248,6 @@ msgstr "创建交易订单失败" msgid "Chart positions" msgstr "图表位置" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Shift request" -msgstr "" - #: src/components/Exchange/ConfirmationBox.js msgid "The order will only execute if the price conditions are met and there is sufficient liquidity" msgstr "该订单只在满足价格条件并有足够的流动性时才会执行" @@ -2177,6 +2173,10 @@ msgstr "已存取{0} USD 至 {tokenSymbol} {longOrShortText}" msgid "Receiver has staked GMX/GLP before" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling sell request" +msgstr "" + #: src/components/Exchange/TradeHistory.js msgid "{actionDisplay} Order" msgstr "{actionDisplay} 指令" @@ -3501,6 +3501,10 @@ msgstr "净率 / 1小时" msgid "Claim esGMX Rewards" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending sell request" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.js msgid "Structured Products" msgstr "结构化产品" @@ -3895,10 +3899,6 @@ msgstr "输入代码" msgid "Execute Take-Profit Order" msgstr "执行止盈订单" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Buy request" -msgstr "" - #: src/components/Synthetics/StatusNotification/SubaccountNotification.tsx msgid "Subaccount is updated" msgstr "" @@ -4199,6 +4199,10 @@ msgstr "执行价格未满足可接受价格条件" msgid "Reserve Amount" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling buy request" +msgstr "" + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Reason: {0}" msgstr "原因: {0}" @@ -4282,10 +4286,6 @@ msgstr "" msgid "Swap submitted!" msgstr "交易送出!" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Buy request" -msgstr "" - #: src/pages/Ecosystem/Ecosystem.js msgid "GBC NFTs APR tracker and rewards" msgstr "GBC NFTs 年利率跟踪器和奖励" @@ -4547,6 +4547,10 @@ msgstr "价钱" msgid "Realized PnL insufficient for Fees" msgstr "" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Sending shift request" +msgstr "" + #: src/components/Exchange/SwapBox.js #: src/pages/Stake/StakeV1.js msgid "Incorrect Network" @@ -4819,10 +4823,6 @@ msgstr "" msgid "Insufficient liquidity to swap collateral" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Shifting GM from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" -msgstr "" - #: src/components/Synthetics/TradeHistory/keys.ts #: src/domain/synthetics/orders/utils.tsx msgid "Market Decrease" @@ -5780,6 +5780,10 @@ msgstr "归属的GMX未被撤回" msgid "FEES" msgstr "费用" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Shifting from <0><1>GM: {fromIndexName}<2>[{fromPoolName}] to <3><4>GM: {toIndexName}<5>[{toPoolName}]" +msgstr "" + #: src/components/Synthetics/GmList/GmList.tsx msgid "TOTAL SUPPLY" msgstr "" @@ -6101,10 +6105,6 @@ msgstr "" msgid "Buy GMX using FIAT gateways:" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Sending Sell request" -msgstr "" - #: src/domain/synthetics/orders/createDecreaseOrderTxn.ts #: src/domain/synthetics/orders/createIncreaseOrderTxn.ts #: src/domain/synthetics/orders/createSwapOrderTxn.ts @@ -6420,10 +6420,6 @@ msgstr "" msgid "View on Arbitrum" msgstr "" -#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx -msgid "Fulfilling Sell request" -msgstr "" - #: src/domain/synthetics/markets/createShiftTxn.ts msgid "Shift error." msgstr "" @@ -6801,6 +6797,10 @@ msgstr "市场交换失败" msgid "Only letters, numbers and underscores are allowed." msgstr "只允许使用字母、数字和下底线" +#: src/components/Synthetics/StatusNotification/GmStatusNotification.tsx +msgid "Fulfilling shift request" +msgstr "" + #: src/pages/Referrals/Referrals.tsx msgid "Referral code submitted!" msgstr "" From 51cad98251539e168677ff7f3c236402986c652c Mon Sep 17 00:00:00 2001 From: midas-myth Date: Wed, 7 Aug 2024 11:37:19 +0000 Subject: [PATCH 42/62] Update shift logic to handle invalid selected market --- .../GmSwapBox/GmShiftBox/useUpdateMarkets.tsx | 10 ++++- .../Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx | 38 ++----------------- 2 files changed, 11 insertions(+), 37 deletions(-) diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useUpdateMarkets.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useUpdateMarkets.tsx index 7216ecc2cf..ed48b39b26 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useUpdateMarkets.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/useUpdateMarkets.tsx @@ -38,8 +38,14 @@ export function useUpdateMarkets({ ); if (!isSelectedMarketValid) { - // Parent component should switch tab to withdrawal - return; + const someAvailableMarket = shiftAvailableMarkets[0]; + + if (!someAvailableMarket) { + return; + } + + newSelectedMarketAddress = someAvailableMarket.marketTokenAddress; + onSelectMarket(newSelectedMarketAddress); } const isToMarketAvailable = Boolean(toMarketAddress && getByKey(marketsInfoData, toMarketAddress)); diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx index 885f47e67f..77f5712e52 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx @@ -1,9 +1,6 @@ import { msg } from "@lingui/macro"; -import { useEffect, useMemo } from "react"; import { useMarketsInfoData } from "context/SyntheticsStateContext/hooks/globalsHooks"; -import { selectShiftAvailableMarkets } from "context/SyntheticsStateContext/selectors/shiftSelectors"; -import { useSelector } from "context/SyntheticsStateContext/utils"; import { useLocalizedMap } from "lib/i18n"; import { getByKey } from "lib/objects"; import { getGmSwapBoxAvailableModes } from "./getGmSwapBoxAvailableModes"; @@ -35,6 +32,8 @@ const MODE_LABELS = { [Mode.Pair]: msg`Pair`, }; +const OPERATIONS = [Operation.Deposit, Operation.Withdrawal, Operation.Shift]; + export function GmSwapBox(p: GmSwapBoxProps) { const { selectedMarketAddress, operation, mode, onSetMode, onSetOperation, onSelectMarket } = p; @@ -43,47 +42,16 @@ export function GmSwapBox(p: GmSwapBoxProps) { const marketsInfoData = useMarketsInfoData(); const marketInfo = getByKey(marketsInfoData, marketAddress); - const shiftAvailableMarkets = useSelector(selectShiftAvailableMarkets); - - const isSelectedMarketShiftAvailable = useMemo(() => { - return Boolean(shiftAvailableMarkets?.find((market) => market.marketTokenAddress === selectedMarketAddress)); - }, [selectedMarketAddress, shiftAvailableMarkets]); - - const availableOperations = useMemo(() => { - return [ - Operation.Deposit, - Operation.Withdrawal, - { - text: Operation.Shift, - disabled: !isSelectedMarketShiftAvailable, - }, - ]; - }, [isSelectedMarketShiftAvailable]); const availableModes = getGmSwapBoxAvailableModes(operation, marketInfo); - useEffect( - function updateOperation() { - const isSelectedMarketShiftAvailable = Boolean( - shiftAvailableMarkets?.find((market) => market.marketTokenAddress === selectedMarketAddress) - ); - - const isShiftOperation = operation === Operation.Shift; - - if (isShiftOperation && !isSelectedMarketShiftAvailable) { - onSetOperation(Operation.Deposit); - } - }, - [onSetOperation, operation, selectedMarketAddress, shiftAvailableMarkets] - ); - const localizedOperationLabels = useLocalizedMap(OPERATION_LABELS); const localizedModeLabels = useLocalizedMap(MODE_LABELS); return (
Date: Wed, 7 Aug 2024 11:44:12 +0000 Subject: [PATCH 43/62] Add shift explanation functionality to MarketPoolsPage --- src/locales/de/messages.po | 4 ++++ src/locales/en/messages.po | 4 ++++ src/locales/es/messages.po | 4 ++++ src/locales/fr/messages.po | 4 ++++ src/locales/ja/messages.po | 4 ++++ src/locales/ko/messages.po | 4 ++++ src/locales/pseudo/messages.po | 4 ++++ src/locales/ru/messages.po | 4 ++++ src/locales/zh/messages.po | 4 ++++ src/pages/MarketPoolsPage/MarketPoolsPage.tsx | 12 ++++++++---- 10 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/locales/de/messages.po b/src/locales/de/messages.po index 3ea3974fcd..c130b69ac3 100644 --- a/src/locales/de/messages.po +++ b/src/locales/de/messages.po @@ -478,6 +478,10 @@ msgstr "Migrationspreis" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before staking." msgstr "" +#: src/pages/MarketPoolsPage/MarketPoolsPage.tsx +msgid "Shift GM Tokens between eligible pools without paying buy/sell fees." +msgstr "" + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "This amount of {0} will be sent from your Main Account to your Subaccount to pay for transaction fees." msgstr "" diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index f7b1546fd1..a27634108b 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -478,6 +478,10 @@ msgstr "Migration Price" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before staking." msgstr "<0>Delegate your undelegated {0} GMX DAO<1> voting power before staking." +#: src/pages/MarketPoolsPage/MarketPoolsPage.tsx +msgid "Shift GM Tokens between eligible pools without paying buy/sell fees." +msgstr "Shift GM Tokens between eligible pools without paying buy/sell fees." + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "This amount of {0} will be sent from your Main Account to your Subaccount to pay for transaction fees." msgstr "This amount of {0} will be sent from your Main Account to your Subaccount to pay for transaction fees." diff --git a/src/locales/es/messages.po b/src/locales/es/messages.po index 8b2b7b9807..8a90a62d03 100644 --- a/src/locales/es/messages.po +++ b/src/locales/es/messages.po @@ -478,6 +478,10 @@ msgstr "Precio de Migración" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before staking." msgstr "" +#: src/pages/MarketPoolsPage/MarketPoolsPage.tsx +msgid "Shift GM Tokens between eligible pools without paying buy/sell fees." +msgstr "" + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "This amount of {0} will be sent from your Main Account to your Subaccount to pay for transaction fees." msgstr "" diff --git a/src/locales/fr/messages.po b/src/locales/fr/messages.po index d545f102c3..4beee1600a 100644 --- a/src/locales/fr/messages.po +++ b/src/locales/fr/messages.po @@ -478,6 +478,10 @@ msgstr "Prix de migration" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before staking." msgstr "" +#: src/pages/MarketPoolsPage/MarketPoolsPage.tsx +msgid "Shift GM Tokens between eligible pools without paying buy/sell fees." +msgstr "" + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "This amount of {0} will be sent from your Main Account to your Subaccount to pay for transaction fees." msgstr "" diff --git a/src/locales/ja/messages.po b/src/locales/ja/messages.po index dea61ab415..8c9545cfaa 100644 --- a/src/locales/ja/messages.po +++ b/src/locales/ja/messages.po @@ -478,6 +478,10 @@ msgstr "移行価格" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before staking." msgstr "" +#: src/pages/MarketPoolsPage/MarketPoolsPage.tsx +msgid "Shift GM Tokens between eligible pools without paying buy/sell fees." +msgstr "" + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "This amount of {0} will be sent from your Main Account to your Subaccount to pay for transaction fees." msgstr "" diff --git a/src/locales/ko/messages.po b/src/locales/ko/messages.po index 50a847b269..12c30048b6 100644 --- a/src/locales/ko/messages.po +++ b/src/locales/ko/messages.po @@ -478,6 +478,10 @@ msgstr "이전 가격" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before staking." msgstr "" +#: src/pages/MarketPoolsPage/MarketPoolsPage.tsx +msgid "Shift GM Tokens between eligible pools without paying buy/sell fees." +msgstr "" + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "This amount of {0} will be sent from your Main Account to your Subaccount to pay for transaction fees." msgstr "" diff --git a/src/locales/pseudo/messages.po b/src/locales/pseudo/messages.po index ce020d3060..d1325364e2 100644 --- a/src/locales/pseudo/messages.po +++ b/src/locales/pseudo/messages.po @@ -478,6 +478,10 @@ msgstr "" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before staking." msgstr "" +#: src/pages/MarketPoolsPage/MarketPoolsPage.tsx +msgid "Shift GM Tokens between eligible pools without paying buy/sell fees." +msgstr "" + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "This amount of {0} will be sent from your Main Account to your Subaccount to pay for transaction fees." msgstr "" diff --git a/src/locales/ru/messages.po b/src/locales/ru/messages.po index da8acf5c71..36c91fbdca 100644 --- a/src/locales/ru/messages.po +++ b/src/locales/ru/messages.po @@ -478,6 +478,10 @@ msgstr "Цена Переноса" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before staking." msgstr "" +#: src/pages/MarketPoolsPage/MarketPoolsPage.tsx +msgid "Shift GM Tokens between eligible pools without paying buy/sell fees." +msgstr "" + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "This amount of {0} will be sent from your Main Account to your Subaccount to pay for transaction fees." msgstr "" diff --git a/src/locales/zh/messages.po b/src/locales/zh/messages.po index 1266f44fd4..3fb836cf74 100644 --- a/src/locales/zh/messages.po +++ b/src/locales/zh/messages.po @@ -478,6 +478,10 @@ msgstr "转移价格" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before staking." msgstr "" +#: src/pages/MarketPoolsPage/MarketPoolsPage.tsx +msgid "Shift GM Tokens between eligible pools without paying buy/sell fees." +msgstr "" + #: src/components/Synthetics/SubaccountModal/SubaccountModal.tsx msgid "This amount of {0} will be sent from your Main Account to your Subaccount to pay for transaction fees." msgstr "" diff --git a/src/pages/MarketPoolsPage/MarketPoolsPage.tsx b/src/pages/MarketPoolsPage/MarketPoolsPage.tsx index 11e94528f3..c92bdcfbbe 100644 --- a/src/pages/MarketPoolsPage/MarketPoolsPage.tsx +++ b/src/pages/MarketPoolsPage/MarketPoolsPage.tsx @@ -65,10 +65,14 @@ export function MarketPoolsPage() { title="V2 Pools" isTop subtitle={ - - Purchase GM Tokens to - earn fees from swaps and leverage trading. - + <> + + Purchase GM Tokens{" "} + to earn fees from swaps and leverage trading. + +
+ Shift GM Tokens between eligible pools without paying buy/sell fees. + } qa="pools-page" /> From a97b64577745ba1ff5d611d5a39ddcd4cf014e2a Mon Sep 17 00:00:00 2001 From: midas-myth Date: Thu, 8 Aug 2024 11:31:37 +0000 Subject: [PATCH 44/62] Shift pr fixes --- src/components/Synthetics/GmList/GmList.tsx | 25 ++++++------------- .../GmConfirmationBox/GmConfirmationBox.tsx | 4 +-- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/components/Synthetics/GmList/GmList.tsx b/src/components/Synthetics/GmList/GmList.tsx index 1eb6d08cb0..fbbbc54fc3 100644 --- a/src/components/Synthetics/GmList/GmList.tsx +++ b/src/components/Synthetics/GmList/GmList.tsx @@ -16,7 +16,6 @@ import { useSelector } from "context/SyntheticsStateContext/utils"; import { MarketTokensAPRData, MarketsInfoData, - UserEarningsData, getMarketIndexName, getMarketPoolName, getMaxPoolUsd, @@ -66,8 +65,6 @@ export function GmList({ marketsTokensApyData, marketsTokensIncentiveAprData, sh const { isConnected: active } = useAccount(); const currentIcons = getIcons(chainId); const userEarnings = useUserEarnings(chainId); - const { showDebugValues } = useSettings(); - const daysConsidered = useDaysConsideredInMarketsApr(); const { orderBy, direction, getSorterProps } = useSorterHandlers(); const [searchText, setSearchText] = useState(""); const shiftAvailableMarkets = useSelector(selectShiftAvailableMarkets); @@ -186,14 +183,9 @@ export function GmList({ marketsTokensApyData, marketsTokensIncentiveAprData, sh currentData.map((token) => ( @@ -324,28 +316,25 @@ function useFilterSortGmPools({ } function GmListItem({ - marketsInfoData, - tokensData, token, marketsTokensApyData, marketsTokensIncentiveAprData, - userEarnings, - showDebugValues, - daysConsidered, shouldScrollToTop, isShiftAvailable, }: { - marketsInfoData: MarketsInfoData | undefined; - tokensData: TokensData | undefined; token: TokenData; marketsTokensApyData: MarketTokensAPRData | undefined; marketsTokensIncentiveAprData: MarketTokensAPRData | undefined; - userEarnings: UserEarningsData | null; - showDebugValues: boolean; - daysConsidered: number; shouldScrollToTop: boolean | undefined; isShiftAvailable: boolean; }) { + const chainId = useSelector(selectChainId); + const marketsInfoData = useMarketsInfoData(); + const tokensData = useTokensData(); + const userEarnings = useUserEarnings(chainId); + const daysConsidered = useDaysConsideredInMarketsApr(); + const { showDebugValues } = useSettings(); + const market = getByKey(marketsInfoData, token?.address)!; const indexToken = getTokenData(tokensData, market?.indexTokenAddress, "native"); diff --git a/src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx b/src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx index d41096a967..9e17b1e848 100644 --- a/src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx +++ b/src/components/Synthetics/GmSwap/GmConfirmationBox/GmConfirmationBox.tsx @@ -150,7 +150,7 @@ export function GmConfirmationBox({ longTokenAmount !== undefined && longTokenAmount > 0 && longToken && - getNeedTokenApprove(tokensAllowanceData, longToken?.address, longTokenAmount) + getNeedTokenApprove(tokensAllowanceData, longToken.address, longTokenAmount) ) { addresses.push(longToken.address); } @@ -159,7 +159,7 @@ export function GmConfirmationBox({ shortTokenAmount !== undefined && shortTokenAmount > 0 && shortToken && - getNeedTokenApprove(tokensAllowanceData, shortToken?.address, shortTokenAmount) + getNeedTokenApprove(tokensAllowanceData, shortToken.address, shortTokenAmount) ) { addresses.push(shortToken.address); } From 95d1a856b659c5ad2824e1e0c370d0bc4e83ac88 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Thu, 8 Aug 2024 14:55:58 +0000 Subject: [PATCH 45/62] Shifts add max button to "from" token --- .../GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx index 23028e3ac2..1f6aa2293b 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx @@ -101,6 +101,11 @@ export function GmShiftBox({ (fees?.swapPriceImpact?.deltaUsd ?? 0) < 0 && bigMath.abs(fees?.swapPriceImpact?.bps ?? 0n) >= HIGH_PRICE_IMPACT_BPS; + const noAmountSet = amounts?.fromTokenAmount === undefined; + const balanceNotEqualToAmount = selectedToken?.balance !== amounts?.fromTokenAmount; + const hasBalance = selectedToken?.balance !== undefined && selectedToken.balance > 0n; + const selectedTokenShowMaxButton = hasBalance && (noAmountSet || balanceNotEqualToAmount); + const submitState = useShiftSubmitState({ selectedMarketInfo, selectedToken, @@ -141,8 +146,9 @@ export function GmShiftBox({ [submitState] ); - const handleSelectedTokenClickTopRightLabel = useCallback(() => { + const handleSelectedTokenClickMax = useCallback(() => { if (!selectedToken || selectedToken.balance === undefined) return; + setFocusedInput("selectedMarket"); setSelectedMarketText(formatAmountFree(selectedToken.balance, selectedToken.decimals)); }, [selectedToken]); const handleSelectedTokenInputValueChange = useCallback( @@ -164,6 +170,7 @@ export function GmShiftBox({ (marketInfo: MarketInfo): void => setToMarketAddress(marketInfo.marketTokenAddress), [] ); + const handleSubmittedOrClosed = useCallback(() => { setIsConfirmationBoxVisible(false); }, []); @@ -178,7 +185,9 @@ export function GmShiftBox({ topRightValue={formatTokenAmount(selectedToken?.balance, selectedToken?.decimals, "", { useCommas: true, })} - onClickTopRightLabel={handleSelectedTokenClickTopRightLabel} + onClickTopRightLabel={handleSelectedTokenClickMax} + showMaxButton={selectedTokenShowMaxButton} + onClickMax={handleSelectedTokenClickMax} inputValue={selectedMarketText} onInputValueChange={handleSelectedTokenInputValueChange} onFocus={handleSelectedTokenFocus} From e3eb242b34af72d97956a28f728ae50ac007f519 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Thu, 8 Aug 2024 15:08:22 +0000 Subject: [PATCH 46/62] Shift fix swap fee in tooltip --- src/components/Synthetics/GmSwap/GmFees/GmFees.tsx | 8 ++------ src/lib/numbers.ts | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/components/Synthetics/GmSwap/GmFees/GmFees.tsx b/src/components/Synthetics/GmSwap/GmFees/GmFees.tsx index 61041af8f5..255f41eea8 100644 --- a/src/components/Synthetics/GmSwap/GmFees/GmFees.tsx +++ b/src/components/Synthetics/GmSwap/GmFees/GmFees.tsx @@ -68,11 +68,7 @@ export function GmFees(p: Props) { {bigMath.abs(p.uiFee?.deltaUsd ?? 0n) > 0 && ( - UI Fee: - - } + label={t`UI Fee`} value={formatDeltaUsd(p.uiFee?.deltaUsd, p.uiFee?.bps)!} showDollar={false} textClassName="text-red-500" @@ -81,7 +77,7 @@ export function GmFees(p: Props) { {p.shiftFee !== undefined && ( Shift Fee} + label={t`Shift Fee`} value={formatDeltaUsd(p.shiftFee.deltaUsd, p.shiftFee.bps)!} showDollar={false} /> diff --git a/src/lib/numbers.ts b/src/lib/numbers.ts index d2db591d5c..e6f858478c 100644 --- a/src/lib/numbers.ts +++ b/src/lib/numbers.ts @@ -215,7 +215,7 @@ export function formatDeltaUsd( const sign = getPlusOrMinusSymbol(deltaUsd, { showPlusForZero: opts.showPlusForZero }); const exceedingInfo = getLimitedDisplay(deltaUsd, USD_DECIMALS); - const percentageStr = percentage ? ` (${sign}${formatPercentage(bigMath.abs(percentage))})` : ""; + const percentageStr = percentage !== undefined ? ` (${sign}${formatPercentage(bigMath.abs(percentage))})` : ""; const deltaUsdStr = formatAmount(exceedingInfo.value, USD_DECIMALS, 2, true); const symbol = exceedingInfo.symbol ? `${exceedingInfo.symbol} ` : ""; From 8740272c89ae42e15a08334d131c869630c1fa54 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Fri, 9 Aug 2024 10:50:57 +0000 Subject: [PATCH 47/62] Hide gm shift tab when pool cant be shifted --- .../GmDepositWithdrawalBox.tsx | 1 + .../GmSwapBox/GmShiftBox/GmShiftBox.tsx | 1 + .../Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx | 21 ++++++++++++++- .../GmSwapBox/useUpdateByQueryParams.tsx | 27 ++++++++++++++++++- 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx index b68851df98..ebeea2f213 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmDepositWithdrawalBox/GmDepositWithdrawalBox.tsx @@ -608,6 +608,7 @@ export function GmSwapBoxDepositWithdrawal(p: GmSwapBoxProps) { ); useUpdateByQueryParams({ + operation, setOperation: onSetOperation, setMode: onSetMode, onSelectMarket, diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx index 1f6aa2293b..55555fe57a 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx @@ -133,6 +133,7 @@ export function GmShiftBox({ useUpdateTokens({ amounts, selectedToken, toToken, focusedInput, setToMarketText, setSelectedMarketText }); useUpdateByQueryParams({ + operation: Operation.Shift, onSelectMarket, setMode: onSetMode, setOperation: onSetOperation, diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx index 77f5712e52..aa9409ca4b 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx @@ -1,6 +1,9 @@ import { msg } from "@lingui/macro"; +import { useMemo } from "react"; import { useMarketsInfoData } from "context/SyntheticsStateContext/hooks/globalsHooks"; +import { selectShiftAvailableMarkets } from "context/SyntheticsStateContext/selectors/shiftSelectors"; +import { useSelector } from "context/SyntheticsStateContext/utils"; import { useLocalizedMap } from "lib/i18n"; import { getByKey } from "lib/objects"; import { getGmSwapBoxAvailableModes } from "./getGmSwapBoxAvailableModes"; @@ -40,9 +43,25 @@ export function GmSwapBox(p: GmSwapBoxProps) { const marketAddress = selectedMarketAddress; const marketsInfoData = useMarketsInfoData(); + const shiftAvailableMarkets = useSelector(selectShiftAvailableMarkets); const marketInfo = getByKey(marketsInfoData, marketAddress); + const availableOperations = useMemo(() => { + if (shiftAvailableMarkets.length === 0) { + return OPERATIONS; + } + + const isSelectedMarketShiftAvailable = Boolean( + shiftAvailableMarkets.find((market) => market.marketTokenAddress === marketAddress) + ); + + if (!isSelectedMarketShiftAvailable) { + return [Operation.Deposit, Operation.Withdrawal]; + } + + return OPERATIONS; + }, [marketAddress, shiftAvailableMarkets]); const availableModes = getGmSwapBoxAvailableModes(operation, marketInfo); const localizedOperationLabels = useLocalizedMap(OPERATION_LABELS); @@ -51,7 +70,7 @@ export function GmSwapBox(p: GmSwapBoxProps) { return (
void; setMode: (mode: Mode) => void; setFirstTokenAddress?: (address: string | undefined) => void; @@ -35,6 +38,7 @@ export function useUpdateByQueryParams({ }) { const history = useHistory(); const searchParams = useSearchParams(); + const shiftAvailableMarkets = useSelector(selectShiftAvailableMarkets); const chainId = useSelector(selectChainId); const marketsInfo = useSelector(selectMarketsInfoData); @@ -97,6 +101,15 @@ export function useUpdateByQueryParams({ selected in order form ); + + const isCurrentlyShift = currentOperation === Operation.Shift; + const isNewMarketShiftAvailable = shiftAvailableMarkets.find( + (shiftMarket) => shiftMarket.marketTokenAddress === marketInfo.marketTokenAddress + ); + + if (isCurrentlyShift && !isNewMarketShiftAvailable) { + setOperation(Operation.Deposit); + } } } @@ -111,6 +124,18 @@ export function useUpdateByQueryParams({ } } }, - [history, onSelectMarket, searchParams, setOperation, setMode, setFirstTokenAddress, chainId, markets, marketsInfo] + [ + history, + onSelectMarket, + searchParams, + setOperation, + setMode, + setFirstTokenAddress, + chainId, + markets, + marketsInfo, + currentOperation, + shiftAvailableMarkets, + ] ); } From 6a38982d7f543ced5e6ff40e19516b1e7dc17bfe Mon Sep 17 00:00:00 2001 From: midas-myth Date: Fri, 9 Aug 2024 11:56:18 +0000 Subject: [PATCH 48/62] Shifts fix price impact --- src/domain/synthetics/trade/utils/deposit.ts | 4 +- src/domain/synthetics/trade/utils/shift.ts | 49 ++++++++++---------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/domain/synthetics/trade/utils/deposit.ts b/src/domain/synthetics/trade/utils/deposit.ts index 431a32c0fc..6cea9a8019 100644 --- a/src/domain/synthetics/trade/utils/deposit.ts +++ b/src/domain/synthetics/trade/utils/deposit.ts @@ -1,9 +1,9 @@ import { applySwapImpactWithCap, getPriceImpactForSwap, getSwapFee } from "domain/synthetics/fees"; import { MarketInfo, marketTokenAmountToUsd, usdToMarketTokenAmount } from "domain/synthetics/markets"; import { TokenData, convertToTokenAmount, convertToUsd, getMidPrice } from "domain/synthetics/tokens"; -import { DepositAmounts } from "../types"; -import { applyFactor } from "lib/numbers"; import { bigMath } from "lib/bigmath"; +import { applyFactor } from "lib/numbers"; +import { DepositAmounts } from "../types"; export function getDepositAmounts(p: { marketInfo: MarketInfo; diff --git a/src/domain/synthetics/trade/utils/shift.ts b/src/domain/synthetics/trade/utils/shift.ts index cbe40c886c..844b410bef 100644 --- a/src/domain/synthetics/trade/utils/shift.ts +++ b/src/domain/synthetics/trade/utils/shift.ts @@ -1,5 +1,5 @@ +import { marketTokenAmountToUsd, usdToMarketTokenAmount } from "domain/synthetics/markets"; import type { MarketInfo } from "domain/synthetics/markets/types"; -import { convertToUsd, getMidPrice } from "domain/synthetics/tokens"; import type { TokenData } from "domain/synthetics/tokens/types"; import { getDepositAmounts } from "./deposit"; @@ -45,21 +45,19 @@ export function getShiftAmounts({ uiFeeUsd: 0n, swapPriceImpactDeltaUsd: 0n, }; - const fromTokenPrice = getMidPrice(fromToken.prices); - const toTokenPrice = getMidPrice(toToken.prices); if (strategy === "byFromToken") { values.fromTokenAmount = fromTokenAmount; - values.fromTokenUsd = convertToUsd(fromTokenAmount, fromToken.decimals, fromTokenPrice)!; + values.fromTokenUsd = marketTokenAmountToUsd(fromMarketInfo, fromToken, fromTokenAmount); const withdrawalAmounts = getWithdrawalAmounts({ marketInfo: fromMarketInfo, marketToken: fromToken, marketTokenAmount: fromTokenAmount, - uiFeeFactor: uiFeeFactor, strategy: "byMarketToken", longTokenAmount: 0n, shortTokenAmount: 0n, + uiFeeFactor: uiFeeFactor, forShift: true, }); @@ -85,34 +83,34 @@ export function getShiftAmounts({ values.swapPriceImpactDeltaUsd = depositAmounts.swapPriceImpactDeltaUsd; values.toTokenAmount = depositAmounts.marketTokenAmount; - values.toTokenUsd = convertToUsd(depositAmounts.marketTokenAmount, toToken.decimals, toTokenPrice)!; + values.toTokenUsd = marketTokenAmountToUsd(toMarketInfo, toToken, depositAmounts.marketTokenAmount); } else { values.toTokenAmount = toTokenAmount; - values.toTokenUsd = convertToUsd(toTokenAmount, toToken.decimals, toTokenPrice)!; + values.toTokenUsd = marketTokenAmountToUsd(toMarketInfo, toToken, toTokenAmount); - const depositAmounts = getDepositAmounts({ - marketInfo: toMarketInfo, - marketToken: toToken, - marketTokenAmount: toTokenAmount, + const withdrawalAmounts = getWithdrawalAmounts({ + marketInfo: fromMarketInfo, + marketToken: fromToken, strategy: "byMarketToken", - longToken: toMarketInfo.longToken, - shortToken: toMarketInfo.shortToken, + marketTokenAmount: usdToMarketTokenAmount(fromMarketInfo, fromToken, values.toTokenUsd), longTokenAmount: 0n, shortTokenAmount: 0n, - includeLongToken: true, - includeShortToken: true, - uiFeeFactor: 0n, + uiFeeFactor: uiFeeFactor, forShift: true, }); - const withdrawalAmounts = getWithdrawalAmounts({ - marketInfo: fromMarketInfo, - marketToken: fromToken, - longTokenAmount: depositAmounts.longTokenAmount, - shortTokenAmount: depositAmounts.shortTokenAmount, + const depositAmounts = getDepositAmounts({ + marketInfo: toMarketInfo, + marketToken: toToken, strategy: "byCollaterals", + longToken: toMarketInfo.longToken, + longTokenAmount: withdrawalAmounts.longTokenAmount, + shortToken: toMarketInfo.shortToken, + shortTokenAmount: withdrawalAmounts.shortTokenAmount, marketTokenAmount: 0n, - uiFeeFactor: uiFeeFactor, + includeLongToken: true, + includeShortToken: true, + uiFeeFactor: 0n, forShift: true, }); @@ -122,8 +120,11 @@ export function getShiftAmounts({ values.uiFeeUsd = withdrawalAmounts.uiFeeUsd; values.swapPriceImpactDeltaUsd = depositAmounts.swapPriceImpactDeltaUsd; - values.fromTokenAmount = withdrawalAmounts.marketTokenAmount; - values.fromTokenUsd = convertToUsd(withdrawalAmounts.marketTokenAmount, fromToken.decimals, fromTokenPrice)!; + // Hack to try to take price impact into account during reverse calculation + values.fromTokenAmount = + withdrawalAmounts.marketTokenAmount - + usdToMarketTokenAmount(fromMarketInfo, fromToken, values.swapPriceImpactDeltaUsd); + values.fromTokenUsd = marketTokenAmountToUsd(fromMarketInfo, fromToken, values.fromTokenAmount); } return values; From f1fdd5f0fc4f6bddc6cd8b766b3dc660cf7fd1a0 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Fri, 9 Aug 2024 14:08:34 +0000 Subject: [PATCH 49/62] Gm swap box align heigh with market stats --- src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx index aa9409ca4b..d11343fb2b 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmSwapBox.tsx @@ -68,7 +68,7 @@ export function GmSwapBox(p: GmSwapBoxProps) { const localizedModeLabels = useLocalizedMap(MODE_LABELS); return ( -
+
Date: Mon, 12 Aug 2024 12:50:00 +0000 Subject: [PATCH 50/62] refactor: Update GmShiftBox component to use isSingle prop as false --- .../Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx index 55555fe57a..b8d203e129 100644 --- a/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx +++ b/src/components/Synthetics/GmSwap/GmSwapBox/GmShiftBox/GmShiftBox.tsx @@ -251,7 +251,7 @@ export function GmShiftBox({ )} From 86f21fd56700e3e9f7df2e6eebeda59b8df0edf0 Mon Sep 17 00:00:00 2001 From: midas-myth Date: Tue, 13 Aug 2024 13:33:19 +0000 Subject: [PATCH 51/62] feat: Update deposit calculation logic to include swap fees --- src/domain/synthetics/trade/utils/deposit.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/domain/synthetics/trade/utils/deposit.ts b/src/domain/synthetics/trade/utils/deposit.ts index 6cea9a8019..7b5f74c207 100644 --- a/src/domain/synthetics/trade/utils/deposit.ts +++ b/src/domain/synthetics/trade/utils/deposit.ts @@ -69,10 +69,10 @@ export function getDepositAmounts(p: { const totalDepositUsd = values.longTokenUsd + values.shortTokenUsd; if (values.longTokenUsd > 0) { - if (!p.forShift) { - const swapFeeUsd = getSwapFee(marketInfo, values.longTokenUsd, values.swapPriceImpactDeltaUsd > 0); - values.swapFeeUsd = values.swapFeeUsd + swapFeeUsd; - } + const swapFeeUsd = p.forShift + ? 0n + : getSwapFee(marketInfo, values.longTokenUsd, values.swapPriceImpactDeltaUsd > 0); + values.swapFeeUsd = values.swapFeeUsd + swapFeeUsd; const uiFeeUsd = applyFactor(values.longTokenUsd, uiFeeFactor); values.uiFeeUsd = values.uiFeeUsd + uiFeeUsd; @@ -86,16 +86,16 @@ export function getDepositAmounts(p: { tokenOut: shortToken, amount: values.longTokenAmount, priceImpactDeltaUsd: bigMath.mulDiv(values.swapPriceImpactDeltaUsd, values.longTokenUsd, totalDepositUsd), - swapFeeUsd: values.swapFeeUsd, + swapFeeUsd, uiFeeUsd, }); } if (values.shortTokenUsd > 0) { - if (!p.forShift) { - const swapFeeUsd = getSwapFee(marketInfo, values.shortTokenUsd, values.swapPriceImpactDeltaUsd > 0); - values.swapFeeUsd = values.swapFeeUsd + swapFeeUsd; - } + const swapFeeUsd = p.forShift + ? 0n + : getSwapFee(marketInfo, values.shortTokenUsd, values.swapPriceImpactDeltaUsd > 0); + values.swapFeeUsd = values.swapFeeUsd + swapFeeUsd; const uiFeeUsd = applyFactor(values.shortTokenUsd, uiFeeFactor); values.uiFeeUsd = values.uiFeeUsd + uiFeeUsd; @@ -109,7 +109,7 @@ export function getDepositAmounts(p: { tokenOut: longToken, amount: values.shortTokenAmount, priceImpactDeltaUsd: bigMath.mulDiv(values.swapPriceImpactDeltaUsd, values.shortTokenUsd, totalDepositUsd), - swapFeeUsd: values.swapFeeUsd, + swapFeeUsd, uiFeeUsd, }); } From 5a46c61721b1765d989acaafd976eaa21109f9e0 Mon Sep 17 00:00:00 2001 From: micky Date: Fri, 9 Aug 2024 16:04:46 +0300 Subject: [PATCH 52/62] add stake lido apr --- src/components/AprInfo/AprInfo.tsx | 90 ++++++++++--------- src/components/Synthetics/GmList/GmList.tsx | 20 ++++- .../Synthetics/MarketStats/MarketStats.tsx | 12 ++- .../MarketTokenSelector.tsx | 36 +++++++- src/config/ethena.ts | 8 ++ src/domain/stake/useLidoStakeApr.ts | 19 ++++ .../synthetics/markets/useGmMarketsApy.ts | 49 +++++++++- src/locales/de/messages.po | 15 +++- src/locales/en/messages.po | 15 +++- src/locales/es/messages.po | 15 +++- src/locales/fr/messages.po | 15 +++- src/locales/ja/messages.po | 15 +++- src/locales/ko/messages.po | 15 +++- src/locales/pseudo/messages.po | 15 +++- src/locales/ru/messages.po | 15 +++- src/locales/zh/messages.po | 15 +++- src/pages/MarketPoolsPage/MarketPoolsPage.tsx | 4 +- src/pages/Stake/StakeV2.tsx | 3 +- 18 files changed, 296 insertions(+), 80 deletions(-) create mode 100644 src/config/ethena.ts create mode 100644 src/domain/stake/useLidoStakeApr.ts diff --git a/src/components/AprInfo/AprInfo.tsx b/src/components/AprInfo/AprInfo.tsx index c65059af8d..632eb8909c 100644 --- a/src/components/AprInfo/AprInfo.tsx +++ b/src/components/AprInfo/AprInfo.tsx @@ -9,6 +9,8 @@ import { getIsBaseApyReadyToBeShown } from "domain/synthetics/markets/getIsBaseA import { useLpAirdroppedTokenTitle } from "domain/synthetics/tokens/useAirdroppedTokenTitle"; import { useChainId } from "lib/chains"; import { formatAmount } from "lib/numbers"; +import { ETHENA_DASHBOARD_URL, isEthenaSatsIncentivizedMarket } from "config/ethena"; +import { LIDO_APR_DECIMALS } from "domain/stake/useLidoStakeApr"; import ExternalLink from "components/ExternalLink/ExternalLink"; import StatsTooltipRow from "components/StatsTooltip/StatsTooltipRow"; @@ -23,11 +25,13 @@ function getApyReadyToBeShownDate(listingDate: Date): Date { export function AprInfo({ apy, incentiveApr, + lidoApr, showTooltip = true, tokenAddress, }: { apy: bigint | undefined; incentiveApr: bigint | undefined; + lidoApr: bigint | undefined; showTooltip?: boolean; tokenAddress: string; }) { @@ -42,65 +46,63 @@ export function AprInfo({ [listingDate] ); - let totalApr = 0n; - if (!isBaseAprReadyToBeShown) { - totalApr = incentiveApr ?? 0n; - } else { - totalApr = (apy ?? 0n) + (incentiveApr ?? 0n); + let totalApr = (incentiveApr ?? 0n) + (lidoApr ?? 0n); + if (isBaseAprReadyToBeShown) { + totalApr += apy ?? 0n; } const incentivesData = useLiquidityProvidersIncentives(chainId); - const isIncentiveActive = !!incentivesData; - const isIncentiveActiveForToken = incentivesData?.rewardsPerMarket[tokenAddress] !== undefined; + const isIncentiveActive = !!incentivesData && incentivesData?.rewardsPerMarket[tokenAddress] !== undefined; + const isLidoApr = lidoApr !== undefined && lidoApr > 0n; + const isEthenaSatsIncentive = isEthenaSatsIncentivizedMarket(tokenAddress); + const airdropTokenTitle = useLpAirdroppedTokenTitle(); const renderTooltipContent = useCallback(() => { - if (!isIncentiveActive || !isIncentiveActiveForToken) { - return ( - <> + return ( +
+
- {!isBaseAprReadyToBeShown && ( - <> -
- - The base APY estimate will be available{" "} - {formatDistanceToNowStrict(apyReadyToBeShownDate as Date, { addSuffix: true })} to ensure accurate data - display. - - + {isIncentiveActive && ( + )} - - ); - } - - return ( - <> - - -
+ {isLidoApr && ( + + )} +
{!isBaseAprReadyToBeShown && ( - <> +
The base APY estimate will be available{" "} {formatDistanceToNowStrict(apyReadyToBeShownDate as Date, { addSuffix: true })} to ensure accurate data display. -
-
- +
+ )} + {isIncentiveActive && ( +
+ + The Bonus APR will be airdropped as {airdropTokenTitle} tokens.{" "} + Read more. + +
+ )} + {isEthenaSatsIncentive && ( +
+ + You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the + Ethena dashboard. + +
)} - - The Bonus APR will be airdropped as {airdropTokenTitle} tokens.{" "} - Read more. - - +
); }, [ airdropTokenTitle, @@ -110,7 +112,9 @@ export function AprInfo({ incentiveApr, isBaseAprReadyToBeShown, isIncentiveActive, - isIncentiveActiveForToken, + lidoApr, + isLidoApr, + isEthenaSatsIncentive, ]); const aprNode = useMemo(() => { @@ -128,7 +132,7 @@ export function AprInfo({ } }, [apy, incentiveApr, totalApr]); - return showTooltip && (isIncentiveActive || !isBaseAprReadyToBeShown) ? ( + return showTooltip && (isIncentiveActive || !isBaseAprReadyToBeShown || isLidoApr || isEthenaSatsIncentive) ? ( @@ -319,12 +327,14 @@ function GmListItem({ token, marketsTokensApyData, marketsTokensIncentiveAprData, + marketsTokensLidoAprData, shouldScrollToTop, isShiftAvailable, }: { token: TokenData; marketsTokensApyData: MarketTokensAPRData | undefined; marketsTokensIncentiveAprData: MarketTokensAPRData | undefined; + marketsTokensLidoAprData: MarketTokensAPRData | undefined; shouldScrollToTop: boolean | undefined; isShiftAvailable: boolean; }) { @@ -344,6 +354,7 @@ function GmListItem({ const apy = getByKey(marketsTokensApyData, token?.address); const incentiveApr = getByKey(marketsTokensIncentiveAprData, token?.address); + const lidoApr = getByKey(marketsTokensLidoAprData, token?.address); const marketEarnings = getByKey(userEarnings?.byMarketAddress, token?.address); if (!token || !indexToken || !longToken || !shortToken) { @@ -408,7 +419,12 @@ function GmListItem({ - + diff --git a/src/components/Synthetics/MarketStats/MarketStats.tsx b/src/components/Synthetics/MarketStats/MarketStats.tsx index 950173798d..f71c10ffac 100644 --- a/src/components/Synthetics/MarketStats/MarketStats.tsx +++ b/src/components/Synthetics/MarketStats/MarketStats.tsx @@ -36,6 +36,7 @@ type Props = { marketToken?: TokenData; marketsTokensApyData: MarketTokensAPRData | undefined; marketsTokensIncentiveAprData: MarketTokensAPRData | undefined; + marketsTokensLidoAprData: MarketTokensAPRData | undefined; }; const MARKET_STATS_DECIMALS = 4; @@ -48,6 +49,7 @@ export function MarketStats(p: Props) { marketsInfoData, marketTokensData, marketsTokensIncentiveAprData, + marketsTokensLidoAprData, } = p; const { chainId } = useChainId(); @@ -80,6 +82,7 @@ export function MarketStats(p: Props) { const apy = getByKey(marketsTokensApyData, marketInfo?.marketTokenAddress); const incentiveApr = getByKey(marketsTokensIncentiveAprData, marketInfo?.marketTokenAddress); + const lidoApr = getByKey(marketsTokensLidoAprData, marketInfo?.marketTokenAddress); const indexName = marketInfo && getMarketIndexName(marketInfo); const poolName = marketInfo && getMarketPoolName(marketInfo); @@ -196,7 +199,14 @@ export function MarketStats(p: Props) { } + value={ + + } /> (); const [searchKeyword, setSearchKeyword] = useState(""); @@ -120,6 +134,7 @@ function MarketTokenSelectorInternal(props: Props) { favoriteTokens, marketsTokensAPRData, marketsTokensIncentiveAprData, + marketsTokensLidoAprData, orderBy, direction, }); @@ -281,6 +296,7 @@ function useFilterSortTokensInfo({ favoriteTokens, marketsTokensAPRData, marketsTokensIncentiveAprData, + marketsTokensLidoAprData, orderBy, direction, }: { @@ -291,6 +307,7 @@ function useFilterSortTokensInfo({ favoriteTokens: string[]; marketsTokensAPRData: MarketTokensAPRData | undefined; marketsTokensIncentiveAprData: MarketTokensAPRData | undefined; + marketsTokensLidoAprData: MarketTokensAPRData | undefined; orderBy: SortField; direction: SortDirection; }) { @@ -324,6 +341,7 @@ function useFilterSortTokensInfo({ const sellableInfo = getSellableMarketToken(marketInfo, market); const apr = getByKey(marketsTokensAPRData, market?.address); const incentiveApr = getByKey(marketsTokensIncentiveAprData, marketInfo?.marketTokenAddress); + const lidoApr = getByKey(marketsTokensLidoAprData, marketInfo?.marketTokenAddress); const indexName = getMarketIndexName(marketInfo); const poolName = getMarketPoolName(marketInfo); return { @@ -335,6 +353,7 @@ function useFilterSortTokensInfo({ poolName, apr, incentiveApr, + lidoApr, }; }); }, [ @@ -344,6 +363,7 @@ function useFilterSortTokensInfo({ marketsTokensIncentiveAprData, searchKeyword, sortedMarketsByIndexToken, + marketsTokensLidoAprData, tab, ]); @@ -395,6 +415,7 @@ function MarketTokenListItem({ tdClassName, apr, incentiveApr, + lidoApr, }: { marketInfo: MarketInfo; market: TokenData; @@ -410,6 +431,7 @@ function MarketTokenListItem({ tdClassName: string; apr: bigint | undefined; incentiveApr: bigint | undefined; + lidoApr: bigint | undefined; }) { const { indexToken, longToken, shortToken } = marketInfo; const iconName = marketInfo.isSpotOnly @@ -467,7 +489,13 @@ function MarketTokenListItem({ {formattedSellableAmount} - + ); diff --git a/src/config/ethena.ts b/src/config/ethena.ts new file mode 100644 index 0000000000..74483b9be2 --- /dev/null +++ b/src/config/ethena.ts @@ -0,0 +1,8 @@ +export const ETHENA_DASHBOARD_URL = "https://app.ethena.fi/join"; + +export const isEthenaSatsIncentivizedMarket = function (market: string) { + return [ + "0x0Cf1fb4d1FF67A3D8Ca92c9d6643F8F9be8e03E5", // ETH/USD [wstETH-USDe] + "0x45aD16Aaa28fb66Ef74d5ca0Ab9751F2817c81a4", // SWAP-ONLY [USDe-USDC] + ].includes(market); +}; diff --git a/src/domain/stake/useLidoStakeApr.ts b/src/domain/stake/useLidoStakeApr.ts new file mode 100644 index 0000000000..59ff660fcf --- /dev/null +++ b/src/domain/stake/useLidoStakeApr.ts @@ -0,0 +1,19 @@ +import useSWR from "swr"; +import { expandDecimals } from "lib/numbers"; + +const LIDO_STAKE_APR_URL = "https://eth-api.lido.fi/v1/protocol/steth/apr/sma"; + +export const LIDO_APR_DECIMALS = 28; +export const LIDO_APR_DIVISOR = expandDecimals(1, LIDO_APR_DECIMALS); + +export function useLidoStakeApr() { + const { data: lidoAprResult } = useSWR(["lido-stake-apr"], { + fetcher: () => fetch(LIDO_STAKE_APR_URL).then((res) => res.json()), + }); + + if (!lidoAprResult?.data.smaApr) { + return undefined; + } + + return BigInt(Math.ceil(lidoAprResult.data.smaApr * Number(LIDO_APR_DIVISOR))); +} diff --git a/src/domain/synthetics/markets/useGmMarketsApy.ts b/src/domain/synthetics/markets/useGmMarketsApy.ts index 99525f28e4..9e83249df6 100644 --- a/src/domain/synthetics/markets/useGmMarketsApy.ts +++ b/src/domain/synthetics/markets/useGmMarketsApy.ts @@ -9,6 +9,7 @@ import { getSubsquidGraphClient } from "lib/subgraph"; import mapValues from "lodash/mapValues"; import { useCallback, useMemo } from "react"; import useSWR from "swr"; +import { useLidoStakeApr } from "domain/stake/useLidoStakeApr"; import { useLiquidityProvidersIncentives } from "../common/useIncentiveStats"; import { getBorrowingFactorPerPeriod } from "../fees"; import { useTokensDataRequest } from "../tokens"; @@ -16,6 +17,8 @@ import { MarketInfo, MarketTokensAPRData, MarketsInfoData } from "./types"; import { useDaysConsideredInMarketsApr } from "./useDaysConsideredInMarketsApr"; import { useMarketTokensData } from "./useMarketTokensData"; import { useMarketsInfoRequest } from "./useMarketsInfoRequest"; +import { getPoolUsdWithoutPnl } from "domain/synthetics/markets"; +import { getTokenBySymbolSafe } from "config/tokens"; import TokenAbi from "abis/Token.json"; @@ -30,7 +33,7 @@ type RawPoolValue = { type GmTokensAPRResult = { marketsTokensIncentiveAprData?: MarketTokensAPRData; - + marketsTokensLidoAprData?: MarketTokensAPRData; marketsTokensApyData?: MarketTokensAPRData; avgMarketsApy?: bigint; }; @@ -38,6 +41,7 @@ type GmTokensAPRResult = { type SwrResult = { marketsTokensApyData: MarketTokensAPRData; avgMarketsApy: bigint; + marketsTokensLidoAprData: MarketTokensAPRData; }; function useMarketAddresses(marketsInfoData: MarketsInfoData | undefined) { @@ -209,6 +213,7 @@ export function useGmMarketsApy(chainId: number): GmTokensAPRResult { marketAddresses.length && marketTokensData && client ? marketAddresses.concat("apr-subsquid").join(",") : null; const daysConsidered = useDaysConsideredInMarketsApr(); + const lidoApr = useLidoStakeApr(); const { data } = useSWR(key, { fetcher: async (): Promise => { @@ -256,6 +261,7 @@ export function useGmMarketsApy(chainId: number): GmTokensAPRResult { if (!responseOrNull) { return { + marketsTokensLidoAprData: {}, marketsTokensApyData: {}, avgMarketsApy: 0n, }; @@ -295,6 +301,45 @@ export function useGmMarketsApy(chainId: number): GmTokensAPRResult { return acc; }, {} as MarketTokensAPRData); + const wstEthToken = getTokenBySymbolSafe(chainId, "wstETH"); + + const marketsTokensLidoAprData = marketAddresses.reduce((acc, marketAddress) => { + const marketInfo = getByKey(marketsInfoData, marketAddress); + if (!marketInfo || !wstEthToken || lidoApr === undefined) return acc; + + const longTokenData = { + address: marketInfo.longTokenAddress, + amount: getPoolUsdWithoutPnl(marketInfo, true, "midPrice"), + }; + + const shortTokenData = { + address: marketInfo.shortTokenAddress, + amount: getPoolUsdWithoutPnl(marketInfo, false, "midPrice"), + }; + + const { incentivesedTokenAmountUsd, otherTokenAmountUsd } = [longTokenData, shortTokenData].reduce( + (amountAcc, { address, amount }) => { + if (address === wstEthToken.address) { + amountAcc.incentivesedTokenAmountUsd += amount; + } else { + amountAcc.otherTokenAmountUsd += amount; + } + return amountAcc; + }, + { incentivesedTokenAmountUsd: 0n, otherTokenAmountUsd: 0n } + ); + + const totalPoolAmountUsd = incentivesedTokenAmountUsd + otherTokenAmountUsd; + + if (incentivesedTokenAmountUsd === 0n || totalPoolAmountUsd === 0n) { + acc[marketAddress] = 0n; + } else { + acc[marketAddress] = bigMath.mulDiv(lidoApr, incentivesedTokenAmountUsd, totalPoolAmountUsd); + } + + return acc; + }, {} as MarketTokensAPRData); + const marketsTokensApyData = mapValues(marketsTokensAPRData, (x) => calculateAPY(x)); const avgMarketsApy = @@ -303,6 +348,7 @@ export function useGmMarketsApy(chainId: number): GmTokensAPRResult { }, 0n) / BigInt(marketAddresses.length); return { + marketsTokensLidoAprData, avgMarketsApy, marketsTokensApyData, }; @@ -312,6 +358,7 @@ export function useGmMarketsApy(chainId: number): GmTokensAPRResult { const marketsTokensIncentiveAprData = useIncentivesBonusApr(chainId, marketsInfoData); return { + marketsTokensLidoAprData: data?.marketsTokensLidoAprData, marketsTokensIncentiveAprData, avgMarketsApy: data?.avgMarketsApy, marketsTokensApyData: data?.marketsTokensApyData, diff --git a/src/locales/de/messages.po b/src/locales/de/messages.po index c130b69ac3..582df223c2 100644 --- a/src/locales/de/messages.po +++ b/src/locales/de/messages.po @@ -945,7 +945,6 @@ msgstr "" msgid "Collateral" msgstr "Kollateral" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "Base APY" msgstr "" @@ -3513,7 +3512,6 @@ msgstr "" msgid "Structured Products" msgstr "Strukturierte Produkte" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx #: src/components/Synthetics/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/Synthetics/PositionSeller/PositionSellerAdvancedDisplayRows.tsx @@ -4487,6 +4485,10 @@ msgstr "" msgid "Execution price for the order." msgstr "Ausführungspreis für die Order." +#: src/components/AprInfo/AprInfo.tsx +msgid "wstETH APR" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.js msgid "Telegram Group" msgstr "Telegram Gruppe" @@ -4634,6 +4636,10 @@ msgstr "WARNUNG: Hohe Gebühren" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before compounding." msgstr "" +#: src/components/AprInfo/AprInfo.tsx +msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the<0>Ethena dashboard." +msgstr "" + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Not enough Available Swap Liquidity to fill the Order. The Order will get filled when the condition is met and there is enough Available Swap Liquidity." msgstr "Nicht genügend verfügbare Swap-Liquidität, um den Auftrag zu erfüllen. Der Auftrag wird ausgeführt, wenn die Bedingung erfüllt ist und genügend verfügbare Swap-Liquidität vorhanden ist." @@ -5177,7 +5183,6 @@ msgstr "" msgid "Claiming failed" msgstr "" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "The base APY estimate will be available {0} to ensure accurate data display." msgstr "" @@ -5832,6 +5837,10 @@ msgstr "" msgid "Could not execute deposit into {0} {longOrShortText}" msgstr "Einzahlung in {0} {longOrShortText} konnte nicht ausgeführt werden" +#: src/components/AprInfo/AprInfo.tsx +#~ msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the <0>Ethena dashboard." +#~ msgstr "" + #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index a27634108b..bebc1fb1bc 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -945,7 +945,6 @@ msgstr "Avalanche Max. APY: {0}" msgid "Collateral" msgstr "Collateral" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "Base APY" msgstr "Base APY" @@ -3516,7 +3515,6 @@ msgstr "Sending sell request" msgid "Structured Products" msgstr "Structured Products" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx #: src/components/Synthetics/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/Synthetics/PositionSeller/PositionSellerAdvancedDisplayRows.tsx @@ -4490,6 +4488,10 @@ msgstr "Long Liquidity" msgid "Execution price for the order." msgstr "Execution price for the order." +#: src/components/AprInfo/AprInfo.tsx +msgid "wstETH APR" +msgstr "wstETH APR" + #: src/pages/Ecosystem/Ecosystem.js msgid "Telegram Group" msgstr "Telegram Group" @@ -4637,6 +4639,10 @@ msgstr "WARNING: High Fees" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before compounding." msgstr "<0>Delegate your undelegated {0} GMX DAO<1> voting power before compounding." +#: src/components/AprInfo/AprInfo.tsx +msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the<0>Ethena dashboard." +msgstr "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the<0>Ethena dashboard." + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Not enough Available Swap Liquidity to fill the Order. The Order will get filled when the condition is met and there is enough Available Swap Liquidity." msgstr "Not enough Available Swap Liquidity to fill the Order. The Order will get filled when the condition is met and there is enough Available Swap Liquidity." @@ -5180,7 +5186,6 @@ msgstr "3rd Place" msgid "Claiming failed" msgstr "Claiming failed" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "The base APY estimate will be available {0} to ensure accurate data display." msgstr "The base APY estimate will be available {0} to ensure accurate data display." @@ -5838,6 +5843,10 @@ msgstr "APRs are updated weekly on Wednesday and will depend on the fees collect msgid "Could not execute deposit into {0} {longOrShortText}" msgstr "Could not execute deposit into {0} {longOrShortText}" +#: src/components/AprInfo/AprInfo.tsx +#~ msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the <0>Ethena dashboard." +#~ msgstr "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the <0>Ethena dashboard." + #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js diff --git a/src/locales/es/messages.po b/src/locales/es/messages.po index 8a90a62d03..ad77e3d146 100644 --- a/src/locales/es/messages.po +++ b/src/locales/es/messages.po @@ -945,7 +945,6 @@ msgstr "" msgid "Collateral" msgstr "Garantía" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "Base APY" msgstr "" @@ -3513,7 +3512,6 @@ msgstr "" msgid "Structured Products" msgstr "Productos Estructurados" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx #: src/components/Synthetics/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/Synthetics/PositionSeller/PositionSellerAdvancedDisplayRows.tsx @@ -4487,6 +4485,10 @@ msgstr "" msgid "Execution price for the order." msgstr "Precio de ejecución para la orden." +#: src/components/AprInfo/AprInfo.tsx +msgid "wstETH APR" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.js msgid "Telegram Group" msgstr "Grupo de Telegram" @@ -4634,6 +4636,10 @@ msgstr "ADVERTENCIA: Altas Comisiones" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before compounding." msgstr "" +#: src/components/AprInfo/AprInfo.tsx +msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the<0>Ethena dashboard." +msgstr "" + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Not enough Available Swap Liquidity to fill the Order. The Order will get filled when the condition is met and there is enough Available Swap Liquidity." msgstr "No hay suficiente liquidez de intercambio disponible para llenar la orden. La orden se llenará cuando se cumpla la condición y haya suficiente liquidez de intercambio disponible." @@ -5177,7 +5183,6 @@ msgstr "" msgid "Claiming failed" msgstr "" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "The base APY estimate will be available {0} to ensure accurate data display." msgstr "" @@ -5832,6 +5837,10 @@ msgstr "" msgid "Could not execute deposit into {0} {longOrShortText}" msgstr "No se pudo ejecutar el depósito en {0} {longOrShortText}" +#: src/components/AprInfo/AprInfo.tsx +#~ msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the <0>Ethena dashboard." +#~ msgstr "" + #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js diff --git a/src/locales/fr/messages.po b/src/locales/fr/messages.po index 4beee1600a..22492c475e 100644 --- a/src/locales/fr/messages.po +++ b/src/locales/fr/messages.po @@ -945,7 +945,6 @@ msgstr "" msgid "Collateral" msgstr "Collatéral" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "Base APY" msgstr "" @@ -3513,7 +3512,6 @@ msgstr "" msgid "Structured Products" msgstr "Produits structurés" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx #: src/components/Synthetics/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/Synthetics/PositionSeller/PositionSellerAdvancedDisplayRows.tsx @@ -4487,6 +4485,10 @@ msgstr "" msgid "Execution price for the order." msgstr "Prix d'exécution pour l'ordre." +#: src/components/AprInfo/AprInfo.tsx +msgid "wstETH APR" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.js msgid "Telegram Group" msgstr "Groupe Telegram" @@ -4634,6 +4636,10 @@ msgstr "AVERTISSEMENT: frais élevés" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before compounding." msgstr "" +#: src/components/AprInfo/AprInfo.tsx +msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the<0>Ethena dashboard." +msgstr "" + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Not enough Available Swap Liquidity to fill the Order. The Order will get filled when the condition is met and there is enough Available Swap Liquidity." msgstr "Pas assez de liquidité d'échange disponible pour remplir l'ordre. L'ordre sera rempli lorsque la condition sera remplie et qu'il y aura suffisamment de liquidité d'échange disponible." @@ -5177,7 +5183,6 @@ msgstr "" msgid "Claiming failed" msgstr "" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "The base APY estimate will be available {0} to ensure accurate data display." msgstr "" @@ -5832,6 +5837,10 @@ msgstr "" msgid "Could not execute deposit into {0} {longOrShortText}" msgstr "Impossible d'exécuter ce dépôt dans {0} {longOrShortText}" +#: src/components/AprInfo/AprInfo.tsx +#~ msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the <0>Ethena dashboard." +#~ msgstr "" + #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js diff --git a/src/locales/ja/messages.po b/src/locales/ja/messages.po index 8c9545cfaa..6e1b687d7b 100644 --- a/src/locales/ja/messages.po +++ b/src/locales/ja/messages.po @@ -945,7 +945,6 @@ msgstr "" msgid "Collateral" msgstr "担保" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "Base APY" msgstr "" @@ -3513,7 +3512,6 @@ msgstr "" msgid "Structured Products" msgstr "ストラクチャードプロダクト" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx #: src/components/Synthetics/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/Synthetics/PositionSeller/PositionSellerAdvancedDisplayRows.tsx @@ -4487,6 +4485,10 @@ msgstr "" msgid "Execution price for the order." msgstr "注文の執行価格" +#: src/components/AprInfo/AprInfo.tsx +msgid "wstETH APR" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.js msgid "Telegram Group" msgstr "Telegramグループ" @@ -4634,6 +4636,10 @@ msgstr "警告: 高い手数料" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before compounding." msgstr "" +#: src/components/AprInfo/AprInfo.tsx +msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the<0>Ethena dashboard." +msgstr "" + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Not enough Available Swap Liquidity to fill the Order. The Order will get filled when the condition is met and there is enough Available Swap Liquidity." msgstr "注文を執行するための利用可能なスワップ流動性が不足しています。条件が満たされ、利用可能なスワップ流動性がある場合に注文が執行されます。" @@ -5177,7 +5183,6 @@ msgstr "" msgid "Claiming failed" msgstr "" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "The base APY estimate will be available {0} to ensure accurate data display." msgstr "" @@ -5832,6 +5837,10 @@ msgstr "" msgid "Could not execute deposit into {0} {longOrShortText}" msgstr "{0} {longOrShortText}への入金を実行できませんでした" +#: src/components/AprInfo/AprInfo.tsx +#~ msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the <0>Ethena dashboard." +#~ msgstr "" + #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js diff --git a/src/locales/ko/messages.po b/src/locales/ko/messages.po index 12c30048b6..0d10e627fb 100644 --- a/src/locales/ko/messages.po +++ b/src/locales/ko/messages.po @@ -945,7 +945,6 @@ msgstr "" msgid "Collateral" msgstr "담보" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "Base APY" msgstr "" @@ -3513,7 +3512,6 @@ msgstr "" msgid "Structured Products" msgstr "구조화금융 프로덕트" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx #: src/components/Synthetics/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/Synthetics/PositionSeller/PositionSellerAdvancedDisplayRows.tsx @@ -4487,6 +4485,10 @@ msgstr "" msgid "Execution price for the order." msgstr "주문의 실행 가격입니다." +#: src/components/AprInfo/AprInfo.tsx +msgid "wstETH APR" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.js msgid "Telegram Group" msgstr "텔레그램 그룹" @@ -4634,6 +4636,10 @@ msgstr "경고: 높은 수수료" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before compounding." msgstr "" +#: src/components/AprInfo/AprInfo.tsx +msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the<0>Ethena dashboard." +msgstr "" + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Not enough Available Swap Liquidity to fill the Order. The Order will get filled when the condition is met and there is enough Available Swap Liquidity." msgstr "주문을 채울만한 충분한 스왑 유동성이 없습니다. 조건이 충족되고 충분한 스왑 유동성이 있을 때 주문이 채워집니다." @@ -5177,7 +5183,6 @@ msgstr "" msgid "Claiming failed" msgstr "" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "The base APY estimate will be available {0} to ensure accurate data display." msgstr "" @@ -5832,6 +5837,10 @@ msgstr "" msgid "Could not execute deposit into {0} {longOrShortText}" msgstr "{0} {longOrShortText}의 입글을 실행시키지 못했습니다" +#: src/components/AprInfo/AprInfo.tsx +#~ msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the <0>Ethena dashboard." +#~ msgstr "" + #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js diff --git a/src/locales/pseudo/messages.po b/src/locales/pseudo/messages.po index d1325364e2..ba46bca54a 100644 --- a/src/locales/pseudo/messages.po +++ b/src/locales/pseudo/messages.po @@ -945,7 +945,6 @@ msgstr "" msgid "Collateral" msgstr "" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "Base APY" msgstr "" @@ -3513,7 +3512,6 @@ msgstr "" msgid "Structured Products" msgstr "" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx #: src/components/Synthetics/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/Synthetics/PositionSeller/PositionSellerAdvancedDisplayRows.tsx @@ -4487,6 +4485,10 @@ msgstr "" msgid "Execution price for the order." msgstr "" +#: src/components/AprInfo/AprInfo.tsx +msgid "wstETH APR" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.js msgid "Telegram Group" msgstr "" @@ -4634,6 +4636,10 @@ msgstr "" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before compounding." msgstr "" +#: src/components/AprInfo/AprInfo.tsx +msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the<0>Ethena dashboard." +msgstr "" + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Not enough Available Swap Liquidity to fill the Order. The Order will get filled when the condition is met and there is enough Available Swap Liquidity." msgstr "" @@ -5177,7 +5183,6 @@ msgstr "" msgid "Claiming failed" msgstr "" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "The base APY estimate will be available {0} to ensure accurate data display." msgstr "" @@ -5832,6 +5837,10 @@ msgstr "" msgid "Could not execute deposit into {0} {longOrShortText}" msgstr "" +#: src/components/AprInfo/AprInfo.tsx +#~ msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the <0>Ethena dashboard." +#~ msgstr "" + #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js diff --git a/src/locales/ru/messages.po b/src/locales/ru/messages.po index 36c91fbdca..b0adbaa8d6 100644 --- a/src/locales/ru/messages.po +++ b/src/locales/ru/messages.po @@ -945,7 +945,6 @@ msgstr "" msgid "Collateral" msgstr "Залог" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "Base APY" msgstr "" @@ -3513,7 +3512,6 @@ msgstr "" msgid "Structured Products" msgstr "Структурированные продукты" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx #: src/components/Synthetics/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/Synthetics/PositionSeller/PositionSellerAdvancedDisplayRows.tsx @@ -4487,6 +4485,10 @@ msgstr "" msgid "Execution price for the order." msgstr "Цена исполнения ордера." +#: src/components/AprInfo/AprInfo.tsx +msgid "wstETH APR" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.js msgid "Telegram Group" msgstr "Группа в Телеграмме" @@ -4634,6 +4636,10 @@ msgstr "ВНИМАНИЕ: высокие тарифы" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before compounding." msgstr "" +#: src/components/AprInfo/AprInfo.tsx +msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the<0>Ethena dashboard." +msgstr "" + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Not enough Available Swap Liquidity to fill the Order. The Order will get filled when the condition is met and there is enough Available Swap Liquidity." msgstr "Недостаточно доступной ликвидности обмена для выполнения ордера. Ордер будет выполнен, когда условие будет выполнено и будет достаточно доступной ликвидности обмена." @@ -5177,7 +5183,6 @@ msgstr "" msgid "Claiming failed" msgstr "" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "The base APY estimate will be available {0} to ensure accurate data display." msgstr "" @@ -5832,6 +5837,10 @@ msgstr "" msgid "Could not execute deposit into {0} {longOrShortText}" msgstr "Не удалось выполнить пополнение счета в {0} {longOrShortText}" +#: src/components/AprInfo/AprInfo.tsx +#~ msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the <0>Ethena dashboard." +#~ msgstr "" + #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js diff --git a/src/locales/zh/messages.po b/src/locales/zh/messages.po index 3fb836cf74..b6349ee339 100644 --- a/src/locales/zh/messages.po +++ b/src/locales/zh/messages.po @@ -945,7 +945,6 @@ msgstr "" msgid "Collateral" msgstr "抵押品" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "Base APY" msgstr "" @@ -3513,7 +3512,6 @@ msgstr "" msgid "Structured Products" msgstr "结构化产品" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx #: src/components/Synthetics/AcceptablePriceImpactInputRow/AcceptablePriceImpactInputRow.tsx #: src/components/Synthetics/PositionSeller/PositionSellerAdvancedDisplayRows.tsx @@ -4487,6 +4485,10 @@ msgstr "" msgid "Execution price for the order." msgstr "指令的执行价格" +#: src/components/AprInfo/AprInfo.tsx +msgid "wstETH APR" +msgstr "" + #: src/pages/Ecosystem/Ecosystem.js msgid "Telegram Group" msgstr "电报群" @@ -4634,6 +4636,10 @@ msgstr "警告:高额费用" msgid "<0>Delegate your undelegated {0} GMX DAO<1> voting power before compounding." msgstr "" +#: src/components/AprInfo/AprInfo.tsx +msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the<0>Ethena dashboard." +msgstr "" + #: src/components/Synthetics/TradeHistory/TradeHistoryRow/utils/shared.ts msgid "Not enough Available Swap Liquidity to fill the Order. The Order will get filled when the condition is met and there is enough Available Swap Liquidity." msgstr "没有足够的可用交换流动性来填补订单。当条件满足并且有足够的可用交换流动性时,订单将被填充。" @@ -5177,7 +5183,6 @@ msgstr "" msgid "Claiming failed" msgstr "" -#: src/components/AprInfo/AprInfo.tsx #: src/components/AprInfo/AprInfo.tsx msgid "The base APY estimate will be available {0} to ensure accurate data display." msgstr "" @@ -5832,6 +5837,10 @@ msgstr "" msgid "Could not execute deposit into {0} {longOrShortText}" msgstr "无法执行存入 {0} {longOrShortText}" +#: src/components/AprInfo/AprInfo.tsx +#~ msgid "You will earn a 20x Ethena sats multiplier per dollar of GM value. Check your earned sats on the <0>Ethena dashboard." +#~ msgstr "" + #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js #: src/components/Exchange/PositionEditor.js diff --git a/src/pages/MarketPoolsPage/MarketPoolsPage.tsx b/src/pages/MarketPoolsPage/MarketPoolsPage.tsx index c92bdcfbbe..728cf9033b 100644 --- a/src/pages/MarketPoolsPage/MarketPoolsPage.tsx +++ b/src/pages/MarketPoolsPage/MarketPoolsPage.tsx @@ -33,7 +33,7 @@ export function MarketPoolsPage() { const depositMarketTokensData = useSelector(selectDepositMarketTokensData); const { marketTokensData: withdrawalMarketTokensData } = useMarketTokensData(chainId, { isDeposit: false }); - const { marketsTokensApyData, marketsTokensIncentiveAprData } = useGmMarketsApy(chainId); + const { marketsTokensApyData, marketsTokensIncentiveAprData, marketsTokensLidoAprData } = useGmMarketsApy(chainId); const [operation, setOperation] = useState(Operation.Deposit); let [mode, setMode] = useState(Mode.Single); @@ -81,6 +81,7 @@ export function MarketPoolsPage() { diff --git a/src/pages/Stake/StakeV2.tsx b/src/pages/Stake/StakeV2.tsx index 001dcf6232..7f02c96bff 100644 --- a/src/pages/Stake/StakeV2.tsx +++ b/src/pages/Stake/StakeV2.tsx @@ -1345,7 +1345,7 @@ export default function StakeV2() { ]; const { marketTokensData } = useMarketTokensData(chainId, { isDeposit: false }); - const { marketsTokensApyData, marketsTokensIncentiveAprData } = useGmMarketsApy(chainId); + const { marketsTokensApyData, marketsTokensIncentiveAprData, marketsTokensLidoAprData } = useGmMarketsApy(chainId); const vestingData = useVestingData(account); const govTokenAmount = useGovTokenAmount(chainId); const govTokenDelegatesAddress = useGovTokenDelegates(chainId); @@ -2400,6 +2400,7 @@ export default function StakeV2() { From 4457e4c956002ef7da0cb936dec83db53e898d3a Mon Sep 17 00:00:00 2001 From: micky Date: Tue, 13 Aug 2024 13:41:49 +0200 Subject: [PATCH 53/62] review fixes --- src/config/lido.ts | 1 + src/domain/stake/useLidoStakeApr.ts | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) create mode 100644 src/config/lido.ts diff --git a/src/config/lido.ts b/src/config/lido.ts new file mode 100644 index 0000000000..96f8d9c0c5 --- /dev/null +++ b/src/config/lido.ts @@ -0,0 +1 @@ +export const LIDO_STAKE_APR_URL = "https://eth-api.lido.fi/v1/protocol/steth/apr/sma"; diff --git a/src/domain/stake/useLidoStakeApr.ts b/src/domain/stake/useLidoStakeApr.ts index 59ff660fcf..d38ae62147 100644 --- a/src/domain/stake/useLidoStakeApr.ts +++ b/src/domain/stake/useLidoStakeApr.ts @@ -1,19 +1,22 @@ import useSWR from "swr"; import { expandDecimals } from "lib/numbers"; - -const LIDO_STAKE_APR_URL = "https://eth-api.lido.fi/v1/protocol/steth/apr/sma"; +import { LIDO_STAKE_APR_URL } from "config/lido"; export const LIDO_APR_DECIMALS = 28; export const LIDO_APR_DIVISOR = expandDecimals(1, LIDO_APR_DECIMALS); export function useLidoStakeApr() { - const { data: lidoAprResult } = useSWR(["lido-stake-apr"], { - fetcher: () => fetch(LIDO_STAKE_APR_URL).then((res) => res.json()), - }); + const { data: lidoApr } = useSWR(["lido-stake-apr"], { + fetcher: async () => { + const lidoResponse = await fetch(LIDO_STAKE_APR_URL).then((res) => res.json()); - if (!lidoAprResult?.data.smaApr) { - return undefined; - } + if (!lidoResponse?.data.smaApr) { + return undefined; + } + + return BigInt(Math.ceil(lidoResponse.data.smaApr * Number(LIDO_APR_DIVISOR))); + }, + }); - return BigInt(Math.ceil(lidoAprResult.data.smaApr * Number(LIDO_APR_DIVISOR))); + return lidoApr; } From 247179dfc6aa6530e8400b543179ebb62a2d2288 Mon Sep 17 00:00:00 2001 From: micky Date: Tue, 13 Aug 2024 15:38:45 +0200 Subject: [PATCH 54/62] fix sort --- src/components/Synthetics/GmList/GmList.tsx | 5 +++++ .../Synthetics/GmList/sortGmTokensByField.tsx | 10 ++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/components/Synthetics/GmList/GmList.tsx b/src/components/Synthetics/GmList/GmList.tsx index 377eef740e..b52f64bc71 100644 --- a/src/components/Synthetics/GmList/GmList.tsx +++ b/src/components/Synthetics/GmList/GmList.tsx @@ -89,6 +89,7 @@ export function GmList({ direction, marketsTokensApyData, marketsTokensIncentiveAprData, + marketsTokensLidoAprData, searchText, tokensData, }); @@ -230,6 +231,7 @@ function useFilterSortGmPools({ direction, marketsTokensApyData, marketsTokensIncentiveAprData, + marketsTokensLidoAprData, searchText, tokensData, }: { @@ -239,6 +241,7 @@ function useFilterSortGmPools({ direction: SortDirection; marketsTokensApyData: MarketTokensAPRData | undefined; marketsTokensIncentiveAprData: MarketTokensAPRData | undefined; + marketsTokensLidoAprData: MarketTokensAPRData | undefined; searchText: string; tokensData: TokensData | undefined; }) { @@ -261,6 +264,7 @@ function useFilterSortGmPools({ direction, marketsTokensApyData, marketsTokensIncentiveAprData, + marketsTokensLidoAprData, }); }, [ chainId, @@ -269,6 +273,7 @@ function useFilterSortGmPools({ marketsInfoData, marketsTokensApyData, marketsTokensIncentiveAprData, + marketsTokensLidoAprData, orderBy, ]); diff --git a/src/components/Synthetics/GmList/sortGmTokensByField.tsx b/src/components/Synthetics/GmList/sortGmTokensByField.tsx index ca64396802..7e7f746eaf 100644 --- a/src/components/Synthetics/GmList/sortGmTokensByField.tsx +++ b/src/components/Synthetics/GmList/sortGmTokensByField.tsx @@ -16,6 +16,7 @@ export function sortGmTokensByField({ direction, marketsTokensApyData, marketsTokensIncentiveAprData, + marketsTokensLidoAprData, }: { chainId: number; marketsInfoData: MarketsInfoData; @@ -24,6 +25,7 @@ export function sortGmTokensByField({ direction: SortDirection; marketsTokensApyData: MarketTokensAPRData | undefined; marketsTokensIncentiveAprData: MarketTokensAPRData | undefined; + marketsTokensLidoAprData: MarketTokensAPRData | undefined; }) { const gmTokens = values(marketTokensData); const directionMultiplier = direction === "asc" ? 1 : -1; @@ -60,12 +62,16 @@ export function sortGmTokensByField({ if (orderBy === "apy") { return gmTokens.sort((a, b) => { - let aprA = marketsTokensIncentiveAprData?.[a.address] ?? 0n; + const bonusAprA = marketsTokensIncentiveAprData?.[a.address] ?? 0n; + const lidoAprA = marketsTokensLidoAprData?.[a.address] ?? 0n; + let aprA = bonusAprA + lidoAprA; if (getIsBaseApyReadyToBeShown(getMarketListingDate(chainId, a.address))) { aprA += marketsTokensApyData?.[a.address] ?? 0n; } - let aprB = marketsTokensIncentiveAprData?.[b.address] ?? 0n; + const bonusAprB = marketsTokensIncentiveAprData?.[b.address] ?? 0n; + const lidoAprB = marketsTokensLidoAprData?.[b.address] ?? 0n; + let aprB = bonusAprB + lidoAprB; if (getIsBaseApyReadyToBeShown(getMarketListingDate(chainId, b.address))) { aprB += marketsTokensApyData?.[b.address] ?? 0n; } From a952b8d7e250670135e29cdbc586e6234cf97224 Mon Sep 17 00:00:00 2001 From: Divhead Date: Tue, 13 Aug 2024 19:10:59 +0300 Subject: [PATCH 55/62] add positions timeout --- .../Synthetics/PositionList/PositionList.tsx | 24 ++++++++++++++++++- src/config/ui.ts | 2 ++ src/context/MetricsContext/types.ts | 7 +++++- src/lib/multicall/useMulticall.ts | 1 + 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/components/Synthetics/PositionList/PositionList.tsx b/src/components/Synthetics/PositionList/PositionList.tsx index ac2312c900..e3878383c1 100644 --- a/src/components/Synthetics/PositionList/PositionList.tsx +++ b/src/components/Synthetics/PositionList/PositionList.tsx @@ -1,5 +1,5 @@ import { Trans, t } from "@lingui/macro"; -import { memo, useCallback, useEffect, useState } from "react"; +import { memo, useCallback, useEffect, useRef, useState } from "react"; import { useIsPositionsLoading, usePositionsInfoData } from "context/SyntheticsStateContext/hooks/globalsHooks"; import { usePositionEditorPositionState } from "context/SyntheticsStateContext/hooks/positionEditorHooks"; @@ -15,6 +15,7 @@ import PositionShare from "components/Exchange/PositionShare"; import { OrderEditorContainer } from "components/OrderEditorContainer/OrderEditorContainer"; import { PositionItem } from "components/Synthetics/PositionItem/PositionItem"; import { useMetrics } from "context/MetricsContext/MetricsContext"; +import { DATA_LOAD_TIMEOUT_FOR_METRICS } from "config/ui"; import { useLatest } from "react-use"; type Props = { @@ -45,13 +46,34 @@ export function PositionList(p: Props) { const isLoading = useIsPositionsLoading(); const metricsRef = useLatest(metrics); + const metricsTimeout = useRef(); useEffect(() => { + if (metricsTimeout.current) { + return; + } + metricsRef.current.startTimer("positionsList"); + + metricsRef.current.sendMetric({ + event: "positionsListLoad.start", + isError: false, + }); + + metricsTimeout.current = setTimeout(() => { + metricsRef.current.sendMetric({ + event: "positionsListLoad.timeout", + message: "Positions list was not loaded", + isError: true, + time: metricsRef.current.getTime("positionsList"), + }); + }, DATA_LOAD_TIMEOUT_FOR_METRICS); }, [metricsRef]); useEffect(() => { if (positionsInfoData && !isLoaded) { + clearTimeout(metricsTimeout.current); + metrics.sendMetric({ event: "positionsListLoad.success", isError: false, diff --git a/src/config/ui.ts b/src/config/ui.ts index 00f564085d..9fc6ff578e 100644 --- a/src/config/ui.ts +++ b/src/config/ui.ts @@ -22,3 +22,5 @@ export const MARKET_STATS_DECIMALS = 4; export const GM_POOL_PRICE_DECIMALS = 4; export const GLP_PRICE_DECIMALS = 4; export const GMX_PRICE_DECIMALS = 2; + +export const DATA_LOAD_TIMEOUT_FOR_METRICS = 10000; diff --git a/src/context/MetricsContext/types.ts b/src/context/MetricsContext/types.ts index 6c29cd8439..c0f2ccc60e 100644 --- a/src/context/MetricsContext/types.ts +++ b/src/context/MetricsContext/types.ts @@ -1,8 +1,13 @@ import { OrderType } from "domain/synthetics/orders"; -export type MetricEventType = OrderEventType | "positionsListLoad.success"; +export type MetricEventType = OrderEventType | PositionsListEventType; export type MetricData = OrderMetricData | OrderWsEventMetricData | PendingTxnErrorMetricData; +export type PositionsListEventType = + | "positionsListLoad.start" + | "positionsListLoad.success" + | "positionsListLoad.timeout"; + export type OrderEventType = `${OrderMetricType}.${OrderStageType}`; export type OrderStageType = "submitted" | "sent" | "created" | "executed" | "cancelled" | "rejected" | "failed"; diff --git a/src/lib/multicall/useMulticall.ts b/src/lib/multicall/useMulticall.ts index 5927695375..48ed3335d7 100644 --- a/src/lib/multicall/useMulticall.ts +++ b/src/lib/multicall/useMulticall.ts @@ -84,6 +84,7 @@ export function useMulticall, TResul } catch (e) { // eslint-disable-next-line no-console console.error(`Multicall request failed: ${name}`, e); + e.message = `Multicall request failed: ${name} ${e.message}`; throw e; } finally { From 9bece8ad6fc0705f89bfef8cae05947476b0fbba Mon Sep 17 00:00:00 2001 From: Divhead Date: Tue, 13 Aug 2024 19:13:27 +0300 Subject: [PATCH 56/62] add positions timeout --- src/components/Synthetics/PositionList/PositionList.tsx | 2 +- src/context/MetricsContext/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Synthetics/PositionList/PositionList.tsx b/src/components/Synthetics/PositionList/PositionList.tsx index e3878383c1..db00276f63 100644 --- a/src/components/Synthetics/PositionList/PositionList.tsx +++ b/src/components/Synthetics/PositionList/PositionList.tsx @@ -56,7 +56,7 @@ export function PositionList(p: Props) { metricsRef.current.startTimer("positionsList"); metricsRef.current.sendMetric({ - event: "positionsListLoad.start", + event: "positionsListLoad.started", isError: false, }); diff --git a/src/context/MetricsContext/types.ts b/src/context/MetricsContext/types.ts index c0f2ccc60e..092418ea5d 100644 --- a/src/context/MetricsContext/types.ts +++ b/src/context/MetricsContext/types.ts @@ -4,7 +4,7 @@ export type MetricEventType = OrderEventType | PositionsListEventType; export type MetricData = OrderMetricData | OrderWsEventMetricData | PendingTxnErrorMetricData; export type PositionsListEventType = - | "positionsListLoad.start" + | "positionsListLoad.started" | "positionsListLoad.success" | "positionsListLoad.timeout"; From 45c58a2bdcb19bbf069815124860d2a91c8371d3 Mon Sep 17 00:00:00 2001 From: Divhead Date: Tue, 13 Aug 2024 20:29:42 +0300 Subject: [PATCH 57/62] fix timers --- src/context/MetricsContext/utils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/context/MetricsContext/utils.ts b/src/context/MetricsContext/utils.ts index 3af0f17058..ca9a951b41 100644 --- a/src/context/MetricsContext/utils.ts +++ b/src/context/MetricsContext/utils.ts @@ -111,6 +111,8 @@ export function sendTxnValidationErrorMetric( export function getTxnSentMetricsHandler(metrics: MetricsContextType, metricId: string, metricType: OrderMetricType) { return () => { + metrics.startTimer(metricId); + metrics.sendMetric({ event: `${metricType}.sent`, isError: false, From 1e4572df9bc5b60d0a32a15fe1ceb4bf2c48cf2d Mon Sep 17 00:00:00 2001 From: An Date: Wed, 14 Aug 2024 08:34:17 +0400 Subject: [PATCH 58/62] fix tests --- src/config/factors.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/config/factors.ts b/src/config/factors.ts index 520a66ee69..de62970c79 100644 --- a/src/config/factors.ts +++ b/src/config/factors.ts @@ -1,4 +1,3 @@ -import { USD_DECIMALS, USDG_DECIMALS } from "lib/legacy"; import { expandDecimals } from "lib/numbers"; export const BASIS_POINTS_DIVISOR = 10000; @@ -27,4 +26,4 @@ export const HIGH_COLLATERAL_IMPACT_BPS = 500; // 5% export const HIGH_SWAP_IMPACT_BPS = 50; // 0.5% export const DEFAULT_ACCEPABLE_PRICE_IMPACT_BUFFER = 30; // 0.3% -export const HIGH_TRADE_VOLUME_FOR_FEEDBACK = expandDecimals(1_000_000, USD_DECIMALS); // 1m +export const HIGH_TRADE_VOLUME_FOR_FEEDBACK = expandDecimals(1_000_000, 30); // 1m From 57d552d33d82406d4298c85535573b8a6756b75a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hub=C3=A9rt=20de=20Lalye?= Date: Wed, 14 Aug 2024 14:35:36 +0400 Subject: [PATCH 59/62] added ORDI and STX --- src/config/events.tsx | 14 ++++++++++++++ src/config/markets.ts | 10 +++++++++- src/config/tokens.ts | 18 ++++++++++++++++++ src/img/ic_ordi_24.svg | 16 ++++++++++++++++ src/img/ic_ordi_40.svg | 15 +++++++++++++++ src/img/ic_stx_24.svg | 11 +++++++++++ src/img/ic_stx_40.svg | 6 ++++++ 7 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/img/ic_ordi_24.svg create mode 100644 src/img/ic_ordi_40.svg create mode 100644 src/img/ic_stx_24.svg create mode 100644 src/img/ic_stx_40.svg diff --git a/src/config/events.tsx b/src/config/events.tsx index 6fe8f4eb45..a1a1b9a93b 100644 --- a/src/config/events.tsx +++ b/src/config/events.tsx @@ -9,6 +9,7 @@ import { getNormalizedTokenSymbol } from "./tokens"; import ExternalLink from "components/ExternalLink/ExternalLink"; import { TokenSymbolWithIcon } from "components/TokenSymbolWithIcon/TokenSymbolWithIcon"; +import { Link } from "react-router-dom"; export type EventData = { id: string; @@ -31,6 +32,19 @@ export type EventData = { export const homeEventsData: EventData[] = []; export const appEventsData: EventData[] = [ + { + id: "ordi-stx-market-arbitrum", + title: "ORDI and STX markets added on Arbitrum", + isActive: true, + startDate: "14 Aug 2024, 00:00", + endDate: "28 Aug 2024, 00:00", + bodyText: ( + <> + Trade ORDI/USD and STX/USD, or provide liquidity to these + pools by using or U. + + ), + }, { id: "shib-market-arbitrum", title: "SHIB/USD [WETH-USDC] market added on Arbitrum", diff --git a/src/config/markets.ts b/src/config/markets.ts index 32b36c36da..e200fe23e8 100644 --- a/src/config/markets.ts +++ b/src/config/markets.ts @@ -6,7 +6,7 @@ import { ARBITRUM, AVALANCHE, BSС_MAINNET } from "./chains"; const p = (date: string) => parse(date, "dd MMM yyyy", new Date()); -export const ENOUGH_DAYS_SINCE_LISTING_FOR_APY = 7; +export const ENOUGH_DAYS_SINCE_LISTING_FOR_APY = 8; const DEFAULT_LISTING = { listingDate: p("01 Jan 1970"), }; @@ -65,6 +65,14 @@ const ENABLED_MARKETS: Record> = { "0xB62369752D8Ad08392572db6d0cc872127888beD": { listingDate: p("7 Aug 2024"), }, + // ORDI/USD [wBTC-USDC] + "0x93385f7c646a3048051914bdfac25f4d620aedf1": { + listingDate: p("14 Aug 2024"), + }, + // STX/USD [wBTC-USDC] + "0xd9377d9b9a2327c7778867203deea73ab8a68b6b": { + listingDate: p("14 Aug 2024"), + }, // SWAP-ONLY [USDC-USDC.e] "0x9C2433dFD71096C435Be9465220BB2B189375eA7": DEFAULT_LISTING, // SWAP-ONLY [USDC-USDT] diff --git a/src/config/tokens.ts b/src/config/tokens.ts index 5966695aab..d0c746b4a0 100644 --- a/src/config/tokens.ts +++ b/src/config/tokens.ts @@ -316,6 +316,24 @@ export const TOKENS: { [chainId: number]: Token[] } = { imageUrl: "https://assets.coingecko.com/coins/images/33566/standard/dogwifhat.jpg?1702499428", coingeckoUrl: "https://www.coingecko.com/en/coins/dogwifhat", }, + { + name: "ORDI", + symbol: "ORDI", + address: "0x1E15d08f3CA46853B692EE28AE9C7a0b88a9c994", + decimals: 18, + imageUrl: "https://assets.coingecko.com/coins/images/30162/standard/ordi.png?1696529082", + coingeckoUrl: "https://www.coingecko.com/en/coins/ordi", + isSynthetic: true, + }, + { + name: "Stacks", + symbol: "STX", + address: "0xBaf07cF91D413C0aCB2b7444B9Bf13b4e03c9D71", + decimals: 6, + imageUrl: "https://assets.coingecko.com/coins/images/2069/standard/Stacks_Logo_png.png?1709979332", + coingeckoUrl: "https://www.coingecko.com/en/coins/stacks", + isSynthetic: true, + }, { name: "Ethena USDe", symbol: "USDe", diff --git a/src/img/ic_ordi_24.svg b/src/img/ic_ordi_24.svg new file mode 100644 index 0000000000..c3426b8f44 --- /dev/null +++ b/src/img/ic_ordi_24.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/img/ic_ordi_40.svg b/src/img/ic_ordi_40.svg new file mode 100644 index 0000000000..d0059efde4 --- /dev/null +++ b/src/img/ic_ordi_40.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/img/ic_stx_24.svg b/src/img/ic_stx_24.svg new file mode 100644 index 0000000000..d9b38833d8 --- /dev/null +++ b/src/img/ic_stx_24.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/img/ic_stx_40.svg b/src/img/ic_stx_40.svg new file mode 100644 index 0000000000..85d4e7fb02 --- /dev/null +++ b/src/img/ic_stx_40.svg @@ -0,0 +1,6 @@ + + + + + + From 3e76a51f649c7753775b32f1133bd7457b2936b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hub=C3=A9rt=20de=20Lalye?= Date: Wed, 14 Aug 2024 16:08:42 +0400 Subject: [PATCH 60/62] udpated ORDI/STX notification style --- src/config/events.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/config/events.tsx b/src/config/events.tsx index a1a1b9a93b..23b7dc6da4 100644 --- a/src/config/events.tsx +++ b/src/config/events.tsx @@ -2,6 +2,7 @@ import { Trans } from "@lingui/macro"; import { type JSX } from "react"; +import { Link } from "react-router-dom"; import { ARBITRUM, AVALANCHE } from "./chains"; import { getIncentivesV2Url } from "./links"; @@ -9,7 +10,6 @@ import { getNormalizedTokenSymbol } from "./tokens"; import ExternalLink from "components/ExternalLink/ExternalLink"; import { TokenSymbolWithIcon } from "components/TokenSymbolWithIcon/TokenSymbolWithIcon"; -import { Link } from "react-router-dom"; export type EventData = { id: string; @@ -40,8 +40,14 @@ export const appEventsData: EventData[] = [ endDate: "28 Aug 2024, 00:00", bodyText: ( <> - Trade ORDI/USD and STX/USD, or provide liquidity to these - pools by using or U. + + Trade + {" "} + ORDI/USD and STX/USD, or{" "} + + provide liquidity + {" "} + to these pools by using or U. ), }, From ea2ca468d0db06a68b7476da3fa6ae59b53baf8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hub=C3=A9rt=20de=20Lalye?= Date: Wed, 14 Aug 2024 16:54:59 +0400 Subject: [PATCH 61/62] fixed toast links style --- src/components/EventToast/EventToast.css | 9 +++++++++ src/config/events.tsx | 10 ++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/components/EventToast/EventToast.css b/src/components/EventToast/EventToast.css index e65febc58f..5ce0b198d1 100644 --- a/src/components/EventToast/EventToast.css +++ b/src/components/EventToast/EventToast.css @@ -80,6 +80,15 @@ padding: 0 1.5rem; } +.single-toast .toast-body a { + text-decoration: underline; + color: var(--color-gray-300); +} + +.single-toast .toast-body a:hover { + color: var(--color-white); +} + @media screen and (max-width: 500px) { .event-toast-container { inset: 8rem 1.6rem 0 1.6rem; diff --git a/src/config/events.tsx b/src/config/events.tsx index 23b7dc6da4..bac11d3021 100644 --- a/src/config/events.tsx +++ b/src/config/events.tsx @@ -40,14 +40,8 @@ export const appEventsData: EventData[] = [ endDate: "28 Aug 2024, 00:00", bodyText: ( <> - - Trade - {" "} - ORDI/USD and STX/USD, or{" "} - - provide liquidity - {" "} - to these pools by using or U. + Trade ORDI/USD and STX/USD, or provide liquidity to these + pools by using or U. ), }, From a7cece0212efba69fa22fdfb652ca905539502be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hub=C3=A9rt=20de=20Lalye?= Date: Wed, 14 Aug 2024 17:23:17 +0400 Subject: [PATCH 62/62] fixed typo in listing notification and markets config --- src/config/events.tsx | 2 +- src/config/markets.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config/events.tsx b/src/config/events.tsx index bac11d3021..7c4c88f7f4 100644 --- a/src/config/events.tsx +++ b/src/config/events.tsx @@ -41,7 +41,7 @@ export const appEventsData: EventData[] = [ bodyText: ( <> Trade ORDI/USD and STX/USD, or provide liquidity to these - pools by using or U. + pools by using or . ), }, diff --git a/src/config/markets.ts b/src/config/markets.ts index e200fe23e8..5803fb55a6 100644 --- a/src/config/markets.ts +++ b/src/config/markets.ts @@ -66,11 +66,11 @@ const ENABLED_MARKETS: Record> = { listingDate: p("7 Aug 2024"), }, // ORDI/USD [wBTC-USDC] - "0x93385f7c646a3048051914bdfac25f4d620aedf1": { + "0x93385F7C646A3048051914BDFaC25F4d620aeDF1": { listingDate: p("14 Aug 2024"), }, // STX/USD [wBTC-USDC] - "0xd9377d9b9a2327c7778867203deea73ab8a68b6b": { + "0xD9377d9B9a2327C7778867203deeA73AB8a68b6B": { listingDate: p("14 Aug 2024"), }, // SWAP-ONLY [USDC-USDC.e]