diff --git a/.env b/.env index acb3efa..e3b0a04 100644 --- a/.env +++ b/.env @@ -2,17 +2,5 @@ VITE_BASE_URL=https://v4.testnet.dydx.exchange VITE_ALCHEMY_API_KEY=FP275q327Yh7qswtZWenbPXdZZdnAFmC -VITE_FAUCET_URL_DEV=https://faucet.v4dev.dydx.exchange/ -VITE_FAUCET_URL_STAGE=https://faucet.v4staging.dydx.exchange/ -VITE_FAUCET_URL_TESTNET2=https://faucet.v4testnet2.dydx.exchange/ - -VITE_VALIDATOR_URL_DEV=http://52.192.187.113:26657/ -VITE_VALIDATOR_URL_STAGE=https://validator.v4staging.dydx.exchange/ -VITE_VALIDATOR_URL_TESTNET2=https://validator.v4testnet2.dydx.exchange/ - -VITE_INDEXER_URL_DEV=http://dev-indexer-apne1-lb-public-890774175.ap-northeast-1.elb.amazonaws.com -VITE_INDEXER_URL_STAGE=https://indexer.v4staging.dydx.exchange -VITE_INDEXER_URL_TESTNET2=https://indexer.v4testnet2.dydx.exchange - VITE_WALLETCONNECT1_BRIDGE=wss://api.dydx.exchange/wc/ VITE_WALLETCONNECT2_PROJECT_ID=fbe94eaa691fa8d929561f8567062b32 diff --git a/.env.example b/.env.example index 0a69401..c47d6b7 100644 --- a/.env.example +++ b/.env.example @@ -1,17 +1,8 @@ VITE_BASE_URL= VITE_ALCHEMY_API_KEY= + VITE_PK_ENCRYPTION_KEY= + +VITE_WALLETCONNECT1_BRIDGE= VITE_WALLETCONNECT2_PROJECT_ID= - -VITE_FAUCET_URL_DEV= -VITE_FAUCET_URL_STAGE= -VITE_FAUCET_URL_TESTNET2= - -VITE_VALIDATOR_URL_DEV= -VITE_VALIDATOR_URL_STAGE= -VITE_VALIDATOR_URL_TESTNET2= - -VITE_INDEXER_URL_DEV= -VITE_INDEXER_URL_STAGE= -VITE_INDEXER_URL_TESTNET2= diff --git a/.eslintignore b/.eslintignore index 1dd398d..6b2c0fe 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,15 +5,10 @@ vite.config.ts # Temporarily ignore, we will slowly remove each directory as we fix files to follow ESLint rules /src/components -/src/dialogs /src/forms /src/hooks -/src/history.ts -/src/icons /src/lib /src/main.tsx /src/menus /src/pages -/src/state -/src/styles /src/views diff --git a/.eslintrc.json b/.eslintrc.json index ae7f722..943c7b7 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -33,14 +33,11 @@ "jsx-a11y/anchor-is-valid": "off", "jsx-a11y/click-events-have-key-events": "off", "no-continue": "off", - "no-console": [ - "error", - { - "devDependencies": ["./scripts/*.js"] - } - ], + "no-console": ["error"], "no-lonely-if": "off", + "no-multi-assign": "off", "no-nested-ternary": "off", + "no-param-reassign": ["error", { "props": false }], "no-return-assign": "off", "no-return-await": "off", "no-underscore-dangle": "off", @@ -65,6 +62,7 @@ "react/sort-comp": "off", "@typescript-eslint/no-use-before-define": "off", "@typescript-eslint/comma-dangle": "off", + "@typescript-eslint/no-unused-vars": "error", "@typescript-eslint/member-delimiter-style": [ "error", { diff --git a/src/App.tsx b/src/App.tsx index 36bda10..c40ac47 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,11 +10,11 @@ import { AppRoute, DEFAULT_TRADE_ROUTE } from '@/constants/routes'; import { useBreakpoints, useInitializePage, useShouldShowFooter, useAnalytics } from '@/hooks'; import { DydxProvider } from '@/hooks/useDydxClient'; import { AccountsProvider } from '@/hooks/useAccounts'; -import { DialogAreaProvider, useDialogArea } from './hooks/useDialogArea'; -import { LocaleProvider } from './hooks/useLocaleSeparators'; -import { NotificationsProvider } from './hooks/useNotifications'; -import { LocalNotificationsProvider } from './hooks/useLocalNotifications'; -import { SubaccountProvider } from './hooks/useSubaccount'; +import { DialogAreaProvider, useDialogArea } from '@/hooks/useDialogArea'; +import { LocaleProvider } from '@/hooks/useLocaleSeparators'; +import { NotificationsProvider } from '@/hooks/useNotifications'; +import { LocalNotificationsProvider } from '@/hooks/useLocalNotifications'; +import { SubaccountProvider } from '@/hooks/useSubaccount'; import { SquidProvider } from '@/hooks/useSquid'; import { GuardedMobileRoute } from '@/components/GuardedMobileRoute'; @@ -100,6 +100,7 @@ const Content = () => { }; const wrapProvider = (Component: React.ComponentType, props?: any) => { + // eslint-disable-next-line react/display-name return ({ children }: { children: React.ReactNode }) => ( {children} ); diff --git a/src/constants/account.ts b/src/constants/account.ts index f8f911e..8035d10 100644 --- a/src/constants/account.ts +++ b/src/constants/account.ts @@ -1,18 +1,5 @@ -/** - * OnboardingSteps - * 1. Choose between 3 options - * a. Ethereum EOA (current) - * b. Keplr or Other Cosmos (future) - * c. Social (future) - * 2. Key derivation - * a. If wallet has no dYdX Chain transactions and not on whitelist, sign twice (future) - * i. Success - * ii. Signatures don't match error (Wallet is non-deterministic) - * b. Success - * 3. Post Registration items (Only on first onboarding) - * a. Acknowledge Terms - * b. DepositFunds - */ +import type { DydxAddress, EvmAddress } from './wallets'; + export enum OnboardingSteps { ChooseWallet = 'ChooseWallet', KeyDerivation = 'KeyDerivation', @@ -49,14 +36,12 @@ export enum EvmDerivedAccountStatus { Derived, } -import type { DydxAddress, EvmAddress } from './wallets'; - export type EvmDerivedAddresses = { version?: string; [EvmAddress: EvmAddress]: { encryptedSignature?: string; dydxAddress?: DydxAddress; }; -} +}; -export const AMOUNT_RESERVED_FOR_GAS_USDC = 100_000; \ No newline at end of file +export const AMOUNT_RESERVED_FOR_GAS_USDC = 100_000; diff --git a/src/constants/notifications.ts b/src/constants/notifications.ts index 5695617..a550546 100644 --- a/src/constants/notifications.ts +++ b/src/constants/notifications.ts @@ -1,4 +1,4 @@ -import { StatusResponse } from "@0xsquid/sdk"; +import { StatusResponse } from '@0xsquid/sdk'; /** implemented in useNotificationTypes */ export enum NotificationType { @@ -11,7 +11,7 @@ export enum NotificationComponentType {} export type NotificationId = string | number; export type NotificationTypeConfig< - _NotificationId extends NotificationId = string, + NotificationIdType extends NotificationId = string, NotificationUpdateKey = any > = { type: NotificationType; @@ -20,7 +20,7 @@ export type NotificationTypeConfig< useTrigger: (_: { trigger: ( /** Unique ID for the triggered notification */ - id: _NotificationId, + id: NotificationIdType, /** Display data for the triggered notification */ displayData: NotificationDisplayData, @@ -43,7 +43,7 @@ export type NotificationTypeConfig< }) => void; /** Callback for notification action (Toast action button click, NotificationsMenu item click, or native push notification interaction) */ - useNotificationAction?: () => (id: _NotificationId) => any; + useNotificationAction?: () => (id: NotificationIdType) => any; }; export enum NotificationStatus { @@ -65,10 +65,10 @@ export enum NotificationStatus { /** Notification state. Serialized and cached into localStorage. */ export type Notification< - _NotificationId extends NotificationId = string, + NotificationIdType extends NotificationId = string, NotificationUpdateKey = any > = { - id: _NotificationId; + id: NotificationIdType; type: NotificationType; status: NotificationStatus; timestamps: Partial>; diff --git a/src/constants/numbers.ts b/src/constants/numbers.ts index 8a28f00..aa6fd6a 100644 --- a/src/constants/numbers.ts +++ b/src/constants/numbers.ts @@ -1,5 +1,3 @@ -import { getSeparator } from '@/lib/numbers'; - export const USD_DECIMALS = 2; export const SMALL_USD_DECIMALS = 4; diff --git a/src/constants/wallets.ts b/src/constants/wallets.ts index ef0c10f..025040f 100644 --- a/src/constants/wallets.ts +++ b/src/constants/wallets.ts @@ -1,4 +1,6 @@ import type { ExternalProvider } from '@ethersproject/providers'; +import { USDC_DENOM, type onboarding, DYDX_DENOM } from '@dydxprotocol/v4-client-js'; +import type { suggestChain } from 'graz'; import { STRING_KEYS } from '@/constants/localization'; @@ -313,10 +315,6 @@ export type WalletConnection = { }; // dYdX Chain wallets - -import { USDC_DENOM, type onboarding, DYDX_DENOM } from '@dydxprotocol/v4-client-js'; -import type { suggestChain } from 'graz'; - export const COSMOS_DERIVATION_PATH = "m/44'/118'/0'/0/0"; /** diff --git a/src/icons/logo-short.tsx b/src/icons/logo-short.tsx index d4a4902..c618ec4 100644 --- a/src/icons/logo-short.tsx +++ b/src/icons/logo-short.tsx @@ -1,4 +1,4 @@ -const LogoShortIcon: React.FC<{ id?: string }> = ({ id }) => ( +const LogoShortIcon: React.FC<{ id?: string }> = ({ id }: { id?: string }) => ( { - const { notifications, getKey, getDisplayData, markUnseen, markSeen, isMenuOpen, onNotificationAction } = useNotifications(); + const { + notifications, + getKey, + getDisplayData, + markUnseen, + markSeen, + isMenuOpen, + onNotificationAction, + } = useNotifications(); const { isMobile } = useBreakpoints(); return ( - <$ToastArea swipeDirection={isMobile ? 'up' : 'right'} className={className}> + {Object.values(notifications) // Sort by time of first trigger .sort( @@ -53,19 +61,20 @@ export const NotificationsToastArea = ({ className }: StyleProps) => { sensitivity={displayData.toastSensitivity} setIsOpen={(isOpen, isClosedFromTimeout) => { if (!isOpen) - // Toast timer expired without user interaction - if (isClosedFromTimeout) markUnseen(notification); + if (isClosedFromTimeout) + // Toast timer expired without user interaction + markUnseen(notification); // Toast interacted with or dismissed else markSeen(notification); }} lastUpdated={notification.timestamps[notification.status]} /> ))} - + ); }; -const $ToastArea = styled(ToastArea)` +const StyledToastArea = styled(ToastArea)` position: absolute; width: min(16rem, 100%); inset: 0 0 0 auto; diff --git a/src/lib/numbers.ts b/src/lib/numbers.ts index 8676d88..d7527b9 100644 --- a/src/lib/numbers.ts +++ b/src/lib/numbers.ts @@ -48,7 +48,7 @@ export const isNumber = (value: any): value is number => /** * @description Returns null if input is 0 or null, '99+' if input is greater than 99, otherwise original input number */ -export const shorternNumberForDisplay = (num?: number) => +export const shortenNumberForDisplay = (num?: number) => MustBigNumber(num).eq(0) ? null : MustBigNumber(num).gt(99) ? '99+' : num; /** diff --git a/src/pages/trade/HorizontalPanel.tsx b/src/pages/trade/HorizontalPanel.tsx index d3b4d73..6473904 100644 --- a/src/pages/trade/HorizontalPanel.tsx +++ b/src/pages/trade/HorizontalPanel.tsx @@ -31,7 +31,7 @@ import { import { getCurrentMarketAssetId, getCurrentMarketId } from '@/state/perpetualsSelectors'; import { isTruthy } from '@/lib/isTruthy'; -import { shorternNumberForDisplay } from '@/lib/numbers'; +import { shortenNumberForDisplay } from '@/lib/numbers'; import { PositionInfo } from '@/views/PositionInfo'; @@ -73,9 +73,9 @@ export const HorizontalPanel = ({ isOpen = true, setIsOpen }: ElementProps) => { const showCurrentMarket = isTablet || view === PanelView.CurrentMarket; - const fillsTagNumber = shorternNumberForDisplay(showCurrentMarket ? numFills : numTotalFills); + const fillsTagNumber = shortenNumberForDisplay(showCurrentMarket ? numFills : numTotalFills); - const ordersTagNumber = shorternNumberForDisplay( + const ordersTagNumber = shortenNumberForDisplay( showCurrentMarket ? numOpenOrders : numTotalOpenOrders ); @@ -86,7 +86,7 @@ export const HorizontalPanel = ({ isOpen = true, setIsOpen }: ElementProps) => { key: showCurrentMarket ? STRING_KEYS.POSITION : STRING_KEYS.POSITIONS, }), - tag: showCurrentMarket ? null : shorternNumberForDisplay(numTotalPositions), + tag: showCurrentMarket ? null : shortenNumberForDisplay(numTotalPositions), content: showCurrentMarket ? ( @@ -190,7 +190,7 @@ export const HorizontalPanel = ({ isOpen = true, setIsOpen }: ElementProps) => { // value: InfoSection.Payments, // label: stringGetter({ key: STRING_KEYS.PAYMENTS }), - // tag: shorternNumberForDisplay( + // tag: shortenNumberForDisplay( // showCurrentMarket ? numFundingPayments : numTotalFundingPayments // ), // content: ( diff --git a/src/state/_store.ts b/src/state/_store.ts index 6552afc..fd57b66 100644 --- a/src/state/_store.ts +++ b/src/state/_store.ts @@ -1,5 +1,6 @@ import { configureStore } from '@reduxjs/toolkit'; +import abacusStateManager from '@/lib/abacus'; import appMiddleware from './appMiddleware'; import localizationMiddleware from './localizationMiddleware'; import routerMiddleware from './routerMiddleware'; @@ -16,8 +17,6 @@ import { localizationSlice } from './localization'; import { navigationSlice } from './navigation'; import { perpetualsSlice } from './perpetuals'; -import abacusStateManager from '@/lib/abacus'; - export const commandMenuSlices = [layoutSlice, localizationSlice]; export const store = configureStore({ diff --git a/src/state/account.ts b/src/state/account.ts index 5fab6e3..8c50aa1 100644 --- a/src/state/account.ts +++ b/src/state/account.ts @@ -1,5 +1,4 @@ import { createSlice, type PayloadAction } from '@reduxjs/toolkit'; -import _ from 'lodash'; import type { SubaccountFill, diff --git a/src/state/accountSelectors.ts b/src/state/accountSelectors.ts index 4adb879..bd1a212 100644 --- a/src/state/accountSelectors.ts +++ b/src/state/accountSelectors.ts @@ -12,9 +12,8 @@ import { import { OnboardingState } from '@/constants/account'; -import type { RootState } from './_store'; - import { getHydratedTradingData } from '@/lib/orders'; +import type { RootState } from './_store'; import { getCurrentMarketId, getPerpetualMarkets } from './perpetualsSelectors'; import { getAssets } from './assetsSelectors'; @@ -106,11 +105,11 @@ export const getSubaccountOpenOrdersBySideAndPrice = createSelector( [getSubaccountOpenOrders], (openOrders = []) => { const ordersBySide: Partial>> = {}; - for (const order of openOrders) { + openOrders.forEach((order) => { const side = ORDER_SIDES[order.side.name]; const byPrice = (ordersBySide[side] ??= {}); byPrice[order.price] = order; - } + }); return ordersBySide; } ); @@ -123,8 +122,10 @@ export const getOrderDetails = (orderId: string) => createSelector( [getSubaccountOrders, getAssets, getPerpetualMarkets], (orders, assets, perpetualMarkets) => { - const order = orders?.find((order) => order.id === orderId); - return order ? getHydratedTradingData({ data: order, assets, perpetualMarkets }) : undefined; + const matchingOrder = orders?.find((order) => order.id === orderId); + return matchingOrder + ? getHydratedTradingData({ data: matchingOrder, assets, perpetualMarkets }) + : undefined; } ); @@ -178,8 +179,10 @@ export const getFillDetails = (fillId: string) => createSelector( [getSubaccountFills, getAssets, getPerpetualMarkets], (fills, assets, perpetualMarkets) => { - const fill = fills?.find((fill) => fill.id === fillId); - return fill ? getHydratedTradingData({ data: fill, assets, perpetualMarkets }) : undefined; + const matchingFill = fills?.find((fill) => fill.id === fillId); + return matchingFill + ? getHydratedTradingData({ data: matchingFill, assets, perpetualMarkets }) + : undefined; } ); diff --git a/src/state/app.ts b/src/state/app.ts index 7d9b2a8..f456442 100644 --- a/src/state/app.ts +++ b/src/state/app.ts @@ -47,9 +47,5 @@ export const appSlice = createSlice({ }, }); -export const { - initializeLocalization, - initializeWebsocket, - setApiState, - setSelectedNetwork, -} = appSlice.actions; +export const { initializeLocalization, initializeWebsocket, setApiState, setSelectedNetwork } = + appSlice.actions; diff --git a/src/state/dialogsSelectors.ts b/src/state/dialogsSelectors.ts index 595d5bb..5f5d2bf 100644 --- a/src/state/dialogsSelectors.ts +++ b/src/state/dialogsSelectors.ts @@ -2,4 +2,4 @@ import { type RootState } from './_store'; export const getActiveDialog = (state: RootState) => state.dialogs.activeDialog; -export const getActiveTradeBoxDialog = (state: RootState) => state.dialogs.activeTradeBoxDialog; \ No newline at end of file +export const getActiveTradeBoxDialog = (state: RootState) => state.dialogs.activeTradeBoxDialog; diff --git a/src/state/localizationSelectors.ts b/src/state/localizationSelectors.ts index bde6447..7981b20 100644 --- a/src/state/localizationSelectors.ts +++ b/src/state/localizationSelectors.ts @@ -40,7 +40,7 @@ export const getStringGetterForLocaleData = (localeData: LocaleData) => { params?: { [key: string]: string | React.ReactNode }; }): string | Array => { // Fallback to english whenever a key doesn't exist for other languages - let formattedString: string = _.get(localeData, key) || _.get(EN_LOCALE_DATA, key) || ''; + const formattedString: string = _.get(localeData, key) || _.get(EN_LOCALE_DATA, key) || ''; return formatString(formattedString, params); }; diff --git a/src/state/perpetualsSelectors.ts b/src/state/perpetualsSelectors.ts index 17a6d33..2bb2f51 100644 --- a/src/state/perpetualsSelectors.ts +++ b/src/state/perpetualsSelectors.ts @@ -1,11 +1,10 @@ -import { matchPath } from 'react-router-dom'; import { createSelector } from 'reselect'; import { Candle, TradingViewBar } from '@/constants/candles'; -import { TRADE_ROUTE } from '@/constants/routes'; + +import { mapCandle } from '@/lib/tradingView/utils'; import type { RootState } from './_store'; -import { mapCandle } from '@/lib/tradingView/utils'; /** * @param state diff --git a/src/styles/breakpointMixins.ts b/src/styles/breakpointMixins.ts index 03a8b2e..30baea6 100644 --- a/src/styles/breakpointMixins.ts +++ b/src/styles/breakpointMixins.ts @@ -1,4 +1,4 @@ -import styled, { css, type FlattenSimpleInterpolation } from 'styled-components'; +import { css } from 'styled-components'; import breakpoints from './breakpoints'; diff --git a/src/styles/layoutMixins.ts b/src/styles/layoutMixins.ts index fa78624..f33ef85 100644 --- a/src/styles/layoutMixins.ts +++ b/src/styles/layoutMixins.ts @@ -6,8 +6,6 @@ import { type ThemeProps, } from 'styled-components'; -import breakpoints from './breakpoints'; - export const layoutMixins: Record< string, FlattenSimpleInterpolation | FlattenInterpolation> diff --git a/src/styles/tableMixins.ts b/src/styles/tableMixins.ts index 516d9e1..0b9e5b4 100644 --- a/src/styles/tableMixins.ts +++ b/src/styles/tableMixins.ts @@ -5,7 +5,6 @@ import { type ThemeProps, } from 'styled-components'; -import breakpoints from './breakpoints'; import { layoutMixins } from './layoutMixins'; export const tableMixins: Record<