Compare commits

..
Author SHA1 Message Date
Bill He 2d31d46dbd polish 2023-08-29 12:01:43 -07:00
Bill He a4a84bf2a1 polish 2023-08-29 11:36:22 -07:00
Bill He 93c2f6be67 localize 2023-08-29 11:29:06 -07:00
Bill He 048527989c polish 2023-08-29 11:21:37 -07:00
Bill He e9f9b7bd6b misc 2023-08-29 11:10:31 -07:00
Bill He e0bb2046cc remove abacus transfer status 2023-08-29 10:57:59 -07:00
Bill He a8ffd1358f use react context 2023-08-29 10:51:05 -07:00
Bill He cb0bd0f39a move out transfer status hook 2023-08-29 10:11:49 -07:00
Bill He 765f39ec5e Deposit/Withdraw Status Notifications 2023-08-28 23:57:01 -07:00
Bill He e656c219cd temp 2023-08-28 15:32:41 -07:00
Bill He e2596b0654 use diffarrow 2023-08-28 15:22:48 -07:00
Bill He 06734a2f38 fix clear bug 2023-08-28 15:17:18 -07:00
Bill He b1a3fb6dc1 address comments 2023-08-28 11:15:47 -07:00
Bill He de08098409 address comments 2023-08-28 11:15:47 -07:00
Bill He d38d449b58 address comments 2023-08-28 11:15:46 -07:00
Bill He 924c56d4f1 fix withdraw chain select 2023-08-28 11:15:46 -07:00
Bill He 0d8f3b4922 working switch network deposits 2023-08-28 11:15:46 -07:00
Bill He ad9e144d40 fix switch network bug 2023-08-28 11:15:46 -07:00
Bill He 78c0134daa fix chain select menu 2023-08-28 11:15:46 -07:00
Bill He cd977ab955 remove unused types 2023-08-28 11:15:46 -07:00
Bill He 196185aa7e deprecate usesquidrouter 2023-08-28 11:15:45 -07:00
Bill He 5093823b25 e2e working deposit/withdraws 2023-08-28 11:15:43 -07:00
Bill He 7d7adc8da2 revert package 2023-08-28 11:14:20 -07:00
Bill He aee06a2c55 Deposit/Withdraw with abacus 2023-08-28 11:13:18 -07:00
61 changed files with 1003 additions and 2038 deletions
+3 -22
View File
@@ -4,8 +4,6 @@
- Node.js version 18 and `pnpm` installed on your system
For deploying with Vercel, create an account with [Vercel](https://vercel.com/signup) if you don't have one already.
For deploying to IPFS, choose one of the following:
- **Option 1:** A free [web3.storage](https://web3.storage/) account
@@ -53,24 +51,7 @@ pnpm ladle
This will automatically open your default browser at `http://localhost:61000`.
## Part 3: Deploying with Vercel
### Step 1: Connect your repository to Vercel
Select "Import Git Repository" from your dashboard, and provide the URL of this repository or your forked repository.
### Step 2: Configure your project
For the "Build & Development Settings", we recommend the following:
- Framework Preset: `Vite`
- Build Command (override): `pnpm run build`
If you wish to incorporate analytics via Amplitude and Bugsnag, you can use our scripts:
`pnpm run build:inject-amplitude` and `pnpm run build:inject-bugsnag`. You will need to provide your own API keys for these services. In the Environment Variables section, name the variables as `AMPLITUDE_API_KEY` and `BUGSNAG_API_KEY` and provide the respective keys as their values.
For more details, check out Vercel's [official documentation](https://vercel.com/docs).
## Part 4: Deploying to IPFS
## Part 3: Deploying to IPFS
### web3.storage: deploy to IPFS via web3.storage using the provided script
@@ -93,7 +74,7 @@ ipfs add -r dist
Save the CID provided in the output.
### Accessing your content on IPFS
## Part 4: Accessing your content on IPFS
To access your content on IPFS:
@@ -101,4 +82,4 @@ To access your content on IPFS:
2. **Public IPFS gateway:** Access your content via a public IPFS gateway, such as [https://dweb.link](https://dweb.link/) or [https://w3s.link/](https://w3s.link/). Use the gateway URL with your CID appended, like `https://dweb.link/ipfs/your_cid`.
Replace `your_cid` with the actual CID.
Replace `your_cid` with the actual CID.
+3 -4
View File
@@ -29,16 +29,15 @@
},
"packageManager": "pnpm@8.6.6",
"dependencies": {
"@0xsquid/sdk": "^1.7.2",
"@0xsquid/sdk": "^1.10.0",
"@cosmjs/amino": "^0.31.0",
"@cosmjs/crypto": "^0.31.0",
"@cosmjs/encoding": "^0.31.0",
"@cosmjs/proto-signing": "^0.31.0",
"@cosmjs/stargate": "^0.31.0",
"@dydxprotocol/abacus": "^0.2.37",
"@cosmjs/tendermint-rpc": "^0.31.0",
"@dydxprotocol/abacus": "^0.3.10",
"@dydxprotocol/v4-client-js": "^0.30.3",
"@dydxprotocol/v4-localization": "^0.0.16",
"@dydxprotocol/v4-client": "^0.29.0",
"@ethersproject/providers": "^5.7.2",
"@js-joda/core": "^5.5.3",
"@radix-ui/react-collapsible": "^1.0.3",
+202 -1772
View File
File diff suppressed because it is too large Load Diff
+27 -24
View File
@@ -13,8 +13,9 @@ 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 { SquidProvider } from '@/hooks/useSquidRouter';
import { SquidProvider } from '@/hooks/useSquid';
import { GuardedMobileRoute } from '@/components/GuardedMobileRoute';
@@ -98,29 +99,31 @@ const Content = () => {
);
};
const App = () => (
<QueryClientProvider client={queryClient}>
<GrazProvider>
<WagmiConfig config={config}>
<LocaleProvider>
<DydxProvider>
<AccountsProvider>
<SubaccountProvider>
<NotificationsProvider>
<DialogAreaProvider>
<SquidProvider>
<Content />
</SquidProvider>
</DialogAreaProvider>
</NotificationsProvider>
</SubaccountProvider>
</AccountsProvider>
</DydxProvider>
</LocaleProvider>
</WagmiConfig>
</GrazProvider>
</QueryClientProvider>
);
const wrapProvider = (Component: React.ComponentType<any>, props?: any) => {
return ({ children }: { children: React.ReactNode }) => (
<Component {...props}>{children}</Component>
);
};
const providers = [
wrapProvider(QueryClientProvider, { client: queryClient }),
wrapProvider(GrazProvider),
wrapProvider(WagmiConfig, { config }),
wrapProvider(LocaleProvider),
wrapProvider(DydxProvider),
wrapProvider(AccountsProvider),
wrapProvider(SubaccountProvider),
wrapProvider(SquidProvider),
wrapProvider(LocalNotificationsProvider),
wrapProvider(NotificationsProvider),
wrapProvider(DialogAreaProvider),
];
const App = () => {
return [...providers].reverse().reduce((children, Provider) => {
return <Provider>{children}</Provider>;
}, <Content />);
};
const Styled: Record<string, AnyStyledComponent> = {};
+1 -1
View File
@@ -1,5 +1,5 @@
import type { Story } from '@ladle/react';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { OrderSide } from '@dydxprotocol/v4-client';
import { OrderSideTag } from '@/components/OrderSideTag';
+1 -1
View File
@@ -1,4 +1,4 @@
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { OrderSide } from '@dydxprotocol/v4-client';
import { STRING_KEYS } from '@/constants/localization';
import { useStringGetter } from '@/hooks';
+12 -10
View File
@@ -17,6 +17,7 @@ type ElementProps = {
slotIcon?: React.ReactNode;
slotTitle?: React.ReactNode;
slotDescription?: React.ReactNode;
slotCustomContent?: React.ReactNode;
slotAction?: React.ReactNode;
actionDescription?: string;
actionAltText?: string;
@@ -33,6 +34,7 @@ export const Toast = ({
slotIcon,
slotTitle,
slotDescription,
slotCustomContent,
slotAction,
actionDescription = '',
actionAltText = actionDescription,
@@ -78,12 +80,13 @@ export const Toast = ({
<$Title>{slotTitle}</$Title>
</$Header>
<$Description>{slotDescription}</$Description>
<$Action asChild altText={actionAltText}>
{slotAction}
</$Action>
{!slotCustomContent && <$Description>{slotDescription}</$Description>}
{slotCustomContent}
{actionDescription && (
<$Action asChild altText={actionAltText}>
{slotAction}
</$Action>
)}
</$Container>
</div>
</$Root>
@@ -118,8 +121,7 @@ const $Root = styled(Root)`
align-items: end;
transform-origin: left bottom;
animation:
${keyframes`
animation: ${keyframes`
from {
/* scale: 0; */
grid-template-rows: 0fr; // height transition
@@ -203,8 +205,8 @@ const $Container = styled.div`
// Rules
${popoverMixins.popover}
padding: 1rem;
box-shadow:
0 0 0 var(--border-width) var(--color-border), // border
box-shadow: 0 0 0 var(--border-width) var(--color-border),
// border
0 0 0.5rem 0.1rem var(--color-layer-2); // shadow
${$Root}:focus:not([data-swipe='end']) & {
+1 -10
View File
@@ -1,5 +1,5 @@
import Abacus, { kollections } from '@dydxprotocol/abacus';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { OrderSide } from '@dydxprotocol/v4-client';
import { PositionSide, TradeTypes } from './trade';
import { STRING_KEYS } from './localization';
@@ -36,12 +36,6 @@ export type AbacusThreadingProtocol = Omit<
Abacus.exchange.dydx.abacus.protocols.ThreadingProtocol,
'__doNotUseOrImplementIt'
>;
export type AbacusFileSystemProtocol = Omit<
Abacus.exchange.dydx.abacus.protocols.FileSystemProtocol,
'__doNotUseOrImplementIt'
>;
export type FileLocation = Abacus.exchange.dydx.abacus.protocols.FileLocation;
export type ThreadingType = Abacus.exchange.dydx.abacus.protocols.ThreadingType;
export const CoroutineTimer = Abacus.exchange.dydx.abacus.utils.CoroutineTimer;
@@ -186,9 +180,6 @@ export const RiskLevel = Abacus.exchange.dydx.abacus.utils.RiskLevel;
const riskLevels = [...RiskLevel.values()] as const;
export type RiskLevels = (typeof riskLevels)[number];
// ------ Notifications ------ //
export type AbacusNotification = Abacus.exchange.dydx.abacus.output.Notification;
// ------ Enum Conversions ------ //
type IfEquals<X, Y, A, B> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2
? A
+1
View File
@@ -12,6 +12,7 @@ export enum LocalStorageKey {
NotificationsLastUpdated = 'dydx.NotificationsLastUpdated',
PushNotificationsEnabled = 'dydx.PushNotificationsEnabled',
PushNotificationsLastUpdated = 'dydx.PushNotificationsLastUpdated',
TransferNotifications = 'dydx.TransferNotifications',
// UI State
LastViewedMarket = 'dydx.LastViewedMarket',
+8
View File
@@ -109,6 +109,7 @@ export const APP_STRING_KEYS = {
HEDGIES: 'GENERAL.HEDGIES',
HIDE_SECTION: 'GENERAL.HIDE_SECTION',
HIDE: 'GENERAL.HIDE',
HIDE_DETAILS: 'GENERAL.HIDE_DETAILS',
HISTORY: 'GENERAL.HISTORY',
INCREASED: 'GENERAL.INCREASED',
INCREASING: 'GENERAL.INCREASING',
@@ -383,6 +384,7 @@ export const APP_STRING_KEYS = {
CONNECTING_SUBTITLE: 'ONBOARDING.CONNECTING_SUBTITLE',
COULD_NOT_FIND_AFFILIATE: 'ONBOARDING.COULD_NOT_FIND_AFFILIATE',
CREATE_OR_VERIFY_COSMOS_ADDRESS: 'ONBOARDING.CREATE_OR_VERIFY_COSMOS_ADDRESS',
DEPOSIT_STATUS: 'ONBOARDING.DEPOSIT_STATUS',
DONT_MISS: 'ONBOARDING.DONT_MISS',
ENABLE_API: 'ONBOARDING.ENABLE_API',
ENABLE_TRADING: 'ONBOARDING.ENABLE_TRADING',
@@ -445,6 +447,7 @@ export const APP_STRING_KEYS = {
VERIFY_WALLET_COMPATIBILITY: 'ONBOARDING.VERIFY_WALLET_COMPATIBILITY',
WELCOME: 'ONBOARDING.WELCOME',
WELCOME_DYDX: 'ONBOARDING.WELCOME_DYDX',
WITHDRAW_STATUS: 'ONBOARDING.WITHDRAW_STATUS',
WRONG_NETWORK_EXPLANATION_PRODUCTION: 'ONBOARDING.WRONG_NETWORK_EXPLANATION_PRODUCTION',
WRONG_NETWORK_EXPLANATION_STAGING: 'ONBOARDING.WRONG_NETWORK_EXPLANATION_STAGING',
WRONG_NETWORK: 'ONBOARDING.WRONG_NETWORK',
@@ -900,6 +903,7 @@ export const APP_STRING_KEYS = {
'66_REDUCTION': 'NOTIFICATIONS.66_REDUCTION',
ADJUSTED_IMR: 'NOTIFICATIONS.ADJUSTED_IMR',
ADJUSTED_IMR_BODY: 'NOTIFICATIONS.ADJUSTED_IMR_BODY',
BRIDGING_TOKENS: "NOTIFICATIONS.BRIDGING_TOKENS",
CLOSE_MARKET_POSITIONS: 'NOTIFICATIONS.CLOSE_MARKET_POSITIONS',
CLOSE_MARKET_POSITIONS_BODY: 'NOTIFICATIONS.CLOSE_MARKET_POSITIONS_BODY',
COMPLIANCE_ALERT: 'NOTIFICATIONS.COMPLIANCE_ALERT',
@@ -907,6 +911,7 @@ export const APP_STRING_KEYS = {
DEPOSIT_IN_PROGRESS: 'NOTIFICATIONS.DEPOSIT_IN_PROGRESS',
DEPOSIT_SUCCESS_DESCRIPTION: 'NOTIFICATIONS.DEPOSIT_SUCCESS_DESCRIPTION',
DEPOSIT_SUCCESS: 'NOTIFICATIONS.DEPOSIT_SUCCESS',
DEPOSIT_TO_CHAIN: "NOTIFICATIONS.DEPOSIT_TO_CHAIN",
DIRECT_TRANSFER_SUCCESS: 'NOTIFICATIONS.DIRECT_TRANSFER_SUCCESS',
DIRECT_TRANSFER_SUCCESS_DESCRIPTION: 'NOTIFICATIONS.DIRECT_TRANSFER_SUCCESS_DESCRIPTION',
EPOCH_REWARDS: 'NOTIFICATIONS.EPOCH_REWARDS',
@@ -915,6 +920,8 @@ export const APP_STRING_KEYS = {
FAST_WITHDRAW_PENDING: 'NOTIFICATIONS.FAST_WITHDRAW_PENDING',
FAST_WITHDRAW_SUCCESS_DESCRIPTION: 'NOTIFICATIONS.FAST_WITHDRAW_SUCCESS_DESCRIPTION',
FAST_WITHDRAW_SUCCESS: 'NOTIFICATIONS.FAST_WITHDRAW_SUCCESS',
INITIATED_DEPOSIT: "NOTIFICATIONS.INITIATED_DEPOSIT",
INITIATED_WITHDRAWAL: "NOTIFICATIONS.INITIATED_WITHDRAWAL",
LEGAL_UPDATES: 'NOTIFICATIONS.LEGAL_UPDATES',
LEGAL_UPDATES_DESCRIPTION: 'NOTIFICATIONS.LEGAL_UPDATES_DESCRIPTION',
MAINTENANCE_MARGIN_ADJUSTMENTS: 'NOTIFICATIONS.MAINTENANCE_MARGIN_ADJUSTMENTS',
@@ -932,6 +939,7 @@ export const APP_STRING_KEYS = {
SUSPICIOUS_TRADE: 'NOTIFICATIONS.SUSPICIOUS_TRADE',
SUSPICIOUS_TRADE_BODY: 'NOTIFICATIONS.SUSPICIOUS_TRADE_BODY',
WITHDRAW_IN_PROGRESS: 'NOTIFICATIONS.WITHDRAW_IN_PROGRESS',
WITHDRAW_TO_CHAIN: "NOTIFICATIONS.WITHDRAW_TO_CHAIN",
// Email Notifications
ACCOUNT_DESCRIPTION: 'EMAIL_NOTIFICATIONS.ACCOUNT_DESCRIPTION',
+62 -18
View File
@@ -1,29 +1,73 @@
import { ENDPOINTS, DEV_ENDPOINTS } from '@dydxprotocol/v4-localization';
export const NETWORK_ENDPOINTS = import.meta.env.MODE === 'production' ? ENDPOINTS : DEV_ENDPOINTS;
export const CLIENT_NETWORK_CONFIGS: Record<
string,
(typeof NETWORK_ENDPOINTS.environments)[number]
> = Object.fromEntries(
NETWORK_ENDPOINTS.environments.map((environment) => [environment.environment, environment])
);
export const DEFAULT_APP_ENVIRONMENT = NETWORK_ENDPOINTS.defaultEnvironment;
export enum DydxV4Network {
V4Mainnet = 'dydxprotocol-mainnet',
V4Staging = 'dydxprotocol-staging',
V4Testnet2 = 'dydxprotocol-testnet',
V4Testnet2 = 'dydxprotocol-testnet-2',
V4Local = 'dydxprotocol-dev',
}
export type DydxNetwork = keyof typeof CLIENT_NETWORK_CONFIGS;
export enum DydxV3Network {
V3Mainnet = '1',
V3Testnet = '5',
}
export function isDydxV4Network(network: any): boolean {
return CLIENT_NETWORK_CONFIGS[network]?.version === 'v4';
export function isDydxV4Network(network: any): network is DydxV4Network {
return Object.values(DydxV4Network).includes(network);
}
export function isValidDydxNetwork(network: any): boolean {
return Object.keys(CLIENT_NETWORK_CONFIGS).includes(network);
return isDydxV4Network(network) || Object.values(DydxV3Network).includes(network);
}
export type DydxNetwork = DydxV4Network | DydxV3Network;
export const CHAIN_IDS = {
[DydxV4Network.V4Mainnet]: '',
[DydxV4Network.V4Staging]: 'dydxprotocol-testnet',
[DydxV4Network.V4Testnet2]: 'dydx-testnet-2',
[DydxV4Network.V4Local]: 'dydxprotocol-testnet',
[DydxV3Network.V3Mainnet]: '1',
[DydxV3Network.V3Testnet]: '5',
};
export const DEFAULT_APP_ENVIRONMENT =
import.meta.env.MODE === 'production' ? DydxV4Network.V4Testnet2 : DydxV4Network.V4Staging;
export const CLIENT_NETWORK_CONFIGS: Record<
DydxV4Network,
{
chainId: string;
validatorUrl: string;
faucetUrl: string;
indexerUrl: string;
ethereumChainId: number;
}
> = {
[DydxV4Network.V4Mainnet]: {
chainId: CHAIN_IDS[DydxV4Network.V4Mainnet],
validatorUrl: import.meta.env.VITE_VALIDATOR_URL_STAGE,
faucetUrl: import.meta.env.VITE_FAUCET_URL_STAGE,
indexerUrl: import.meta.env.VITE_INDEXER_URL_STAGE,
ethereumChainId: 1,
},
[DydxV4Network.V4Local]: {
chainId: CHAIN_IDS[DydxV4Network.V4Local],
validatorUrl: import.meta.env.VITE_VALIDATOR_URL_DEV,
faucetUrl: import.meta.env.VITE_FAUCET_URL_DEV,
indexerUrl: import.meta.env.VITE_INDEXER_URL_DEV,
ethereumChainId: 5,
},
[DydxV4Network.V4Staging]: {
chainId: CHAIN_IDS[DydxV4Network.V4Staging],
validatorUrl: import.meta.env.VITE_VALIDATOR_URL_STAGE,
faucetUrl: import.meta.env.VITE_FAUCET_URL_STAGE,
indexerUrl: import.meta.env.VITE_INDEXER_URL_STAGE,
ethereumChainId: 5,
},
[DydxV4Network.V4Testnet2]: {
chainId: CHAIN_IDS[DydxV4Network.V4Testnet2],
validatorUrl: import.meta.env.VITE_VALIDATOR_URL_TESTNET2,
faucetUrl: import.meta.env.VITE_FAUCET_URL_TESTNET2,
indexerUrl: import.meta.env.VITE_INDEXER_URL_TESTNET2,
ethereumChainId: 5,
},
};
+29 -6
View File
@@ -1,16 +1,22 @@
import { StatusResponse } from "@0xsquid/sdk";
/** implemented in useNotificationTypes */
export enum NotificationType {
OrderStatusChanged = 'OrderStatusChanged',
SquidTransfer = 'SquidTransfer',
}
export enum NotificationComponentType {}
export type NotificationId = string | number
export type NotificationId = string | number;
export type NotificationTypeConfig<_NotificationId extends NotificationId = string, NotificationUpdateKey = any> = {
export type NotificationTypeConfig<
_NotificationId extends NotificationId = string,
NotificationUpdateKey = any
> = {
type: NotificationType;
/** React hook to trigger notifications based on app state */
/** React hook to trigger notifications based on app state */
useTrigger: (_: {
trigger: (
/** Unique ID for the triggered notification */
@@ -30,7 +36,7 @@ export type NotificationTypeConfig<_NotificationId extends NotificationId = stri
* @param true (default): Notification initialized with status NotificationStatus.Triggered
* @param false: Notification initialized with status NotificationStatus.Cleared
*/
isNew?: boolean,
isNew?: boolean
) => void;
lastUpdated: number;
@@ -58,7 +64,10 @@ export enum NotificationStatus {
}
/** Notification state. Serialized and cached into localStorage. */
export type Notification<_NotificationId extends NotificationId = string, NotificationUpdateKey = any> = {
export type Notification<
_NotificationId extends NotificationId = string,
NotificationUpdateKey = any
> = {
id: _NotificationId;
type: NotificationType;
status: NotificationStatus;
@@ -74,7 +83,11 @@ export type NotificationDisplayData = {
title?: string;
description?: string;
description?: React.ReactNode;
customContent?: React.ReactNode;
customMenuContent?: React.ReactNode;
actionDescription?: string;
@@ -101,3 +114,13 @@ export type NotificationDisplayData = {
*/
toastDuration?: number;
};
// Notification types
export type TransferNotifcation = {
txHash: string;
toChainId?: string;
fromChainId?: string;
toAmount?: number;
triggeredAt?: number;
status?: StatusResponse;
};
+1 -1
View File
@@ -1,4 +1,4 @@
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { OrderSide } from '@dydxprotocol/v4-client';
import { AlertType } from '@/constants/alerts';
import { STRING_KEYS } from '@/constants/localization';
+1 -1
View File
@@ -302,7 +302,7 @@ export type WalletConnection = {
// dYdX Chain wallets
import { USDC_DENOM, type Onboarding, DYDX_DENOM } from '@dydxprotocol/v4-client-js';
import { USDC_DENOM, type Onboarding, DYDX_DENOM } from '@dydxprotocol/v4-client';
import type { suggestChain } from 'graz';
export const COSMOS_DERIVATION_PATH = "m/44'/118'/0'/0/0";
+6 -2
View File
@@ -1,5 +1,6 @@
import { useEffect } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom';
import isEmpty from 'lodash/isEmpty';
import { LanguageCode, ResolutionString, widget } from 'public/tradingview/charting_library';
@@ -13,8 +14,9 @@ import { store } from '@/state/_store';
import { getSelectedNetwork } from '@/state/appSelectors';
import { getAppTheme } from '@/state/configsSelectors';
import { getSelectedLocale } from '@/state/localizationSelectors';
import { getCurrentMarketId, getMarketIds } from '@/state/perpetualsSelectors';
import { getMarketIds } from '@/state/perpetualsSelectors';
import { getMarketIdFromLocation } from '@/lib/tradeData';
import { getDydxDatafeed } from '@/lib/tradingView/dydxfeed';
import { getSavedResolution, getWidgetOptions, getWidgetOverrides } from '@/lib/tradingView/utils';
@@ -28,7 +30,8 @@ export const useTradingView = ({
tvWidgetRef: React.MutableRefObject<any>;
setIsChartReady: React.Dispatch<React.SetStateAction<boolean>>;
}) => {
const marketId = useSelector(getCurrentMarketId);
const location = useLocation();
const marketId = getMarketIdFromLocation(location);
const appTheme = useSelector(getAppTheme);
const marketIds = useSelector(getMarketIds, shallowEqual);
const selectedLocale = useSelector(getSelectedLocale);
@@ -47,6 +50,7 @@ export const useTradingView = ({
if (hasMarkets) {
const widgetOptions = getWidgetOptions();
const widgetOverrides = getWidgetOverrides(appTheme);
const options = {
// debug: true,
...widgetOptions,
+5 -2
View File
@@ -5,7 +5,7 @@ import { StargateClient } from '@cosmjs/stargate';
import { useQuery } from 'react-query';
import { formatUnits } from 'viem';
import { CLIENT_NETWORK_CONFIGS } from '@/constants/networks';
import { CLIENT_NETWORK_CONFIGS, type DydxV4Network } from '@/constants/networks';
import { QUANTUM_MULTIPLIER } from '@/constants/numbers';
import { EthereumAddress } from '@/constants/wallets';
@@ -15,6 +15,7 @@ import { MustBigNumber } from '@/lib/numbers';
import { getSelectedNetwork } from '@/state/appSelectors';
import { useAccounts } from './useAccounts';
// import { useDydxClient } from './useDydxClient';
import { usePollNativeTokenBalance } from './usePollNativeTokenBalance';
type UseAccountBalanceProps = {
@@ -48,8 +49,10 @@ export const useAccountBalance = ({
}: UseAccountBalanceProps = {}) => {
const { evmAddress, dydxAddress } = useAccounts();
// TODO: let Abacus / useDydxClient handle EVM chain IDs
// const { networkConfig } = useDydxClient();
const selectedNetwork = useSelector(getSelectedNetwork);
const evmChainId = Number(CLIENT_NETWORK_CONFIGS[selectedNetwork].ethereumChainId);
const evmChainId = Number(CLIENT_NETWORK_CONFIGS[selectedNetwork as DydxV4Network].ethereumChainId);
const evmQuery = useBalance({
enabled: Boolean(!isCosmosChain && addressOrDenom?.startsWith('0x')),
+1 -1
View File
@@ -2,7 +2,7 @@ import { useCallback, useContext, createContext, useEffect, useState, useMemo }
import { useDispatch } from 'react-redux';
import { AES, enc } from 'crypto-js';
import { LocalWallet, USDC_DENOM, type Subaccount } from '@dydxprotocol/v4-client-js';
import { LocalWallet, USDC_DENOM, type Subaccount } from '@dydxprotocol/v4-client';
import { OnboardingGuard, OnboardingState, type EvmDerivedAddresses } from '@/constants/account';
import { LocalStorageKey, LOCAL_STORAGE_VERSIONS } from '@/constants/localStorage';
+1 -1
View File
@@ -29,7 +29,7 @@ export const useCurrentMarketId = () => {
const validId = useMemo(() => {
if (marketIds.length === 0) return marketId ?? lastViewedMarket;
if (!marketIds.includes(marketId ?? lastViewedMarket)) return DEFAULT_MARKETID;
if (!marketIds.includes(marketId)) return DEFAULT_MARKETID;
return marketId ?? lastViewedMarket;
}, [marketIds, marketId]);
+4 -2
View File
@@ -1,4 +1,4 @@
import { isDydxV4Network } from '@/constants/networks';
import { DydxV4Network } from '@/constants/networks';
import { WalletType } from '@/constants/wallets';
import { isTruthy } from '@/lib/isTruthy';
@@ -11,7 +11,9 @@ export const useDisplayedWallets = () => {
return [
WalletType.MetaMask,
import.meta.env.MODE !== 'production' && isDydxV4Network(selectedNetwork) && WalletType.Keplr,
import.meta.env.MODE !== 'production' &&
[DydxV4Network.V4Testnet2, DydxV4Network.V4Staging].includes(selectedNetwork) &&
WalletType.Keplr,
WalletType.WalletConnect2,
WalletType.WalletConnect,
+1 -1
View File
@@ -9,7 +9,7 @@ import {
onboarding,
Network,
ValidatorConfig,
} from '@dydxprotocol/v4-client-js';
} from '@dydxprotocol/v4-client';
import type { ResolutionString } from 'public/tradingview/charting_library';
+5 -1
View File
@@ -3,7 +3,11 @@ import { useDispatch } from 'react-redux';
import { LocalStorageKey } from '@/constants/localStorage';
import { DEFAULT_APP_ENVIRONMENT, DydxNetwork, isValidDydxNetwork } from '@/constants/networks';
import {
DEFAULT_APP_ENVIRONMENT,
DydxNetwork,
isValidDydxNetwork,
} from '@/constants/networks';
import { useLocalStorage } from '@/hooks';
+95
View File
@@ -0,0 +1,95 @@
import { createContext, useContext, useCallback, useEffect, useMemo } from 'react';
import { useQuery } from 'react-query';
import { LocalStorageKey } from '@/constants/localStorage';
import { type TransferNotifcation } from '@/constants/notifications';
import { useAccounts } from '@/hooks/useAccounts';
import { useSquid } from '@/hooks/useSquid';
import { useLocalStorage } from './useLocalStorage';
import { StatusResponse } from '@0xsquid/sdk';
const LocalNotificationsContext = createContext<
ReturnType<typeof useLocalNotificationsContext> | undefined
>(undefined);
LocalNotificationsContext.displayName = 'LocalNotifications';
export const LocalNotificationsProvider = ({ ...props }) => (
<LocalNotificationsContext.Provider value={useLocalNotificationsContext()} {...props} />
);
export const useLocalNotifications = () => useContext(LocalNotificationsContext)!;
const TRANSFER_STATUS_FETCH_INTERVAL = 10_000;
const useLocalNotificationsContext = () => {
// transfer notifications
const [allTransferNotifications, setAllTransferNotifications] = useLocalStorage<{
[key: `dydx${string}`]: TransferNotifcation[];
}>({
key: LocalStorageKey.TransferNotifications,
defaultValue: {},
});
const { dydxAddress } = useAccounts();
const transferNotifications = dydxAddress ? allTransferNotifications[dydxAddress] || [] : [];
const setTransferNotifications = useCallback(
(notifications: TransferNotifcation[]) => {
if (!dydxAddress) return;
const updatedNotifications = { ...allTransferNotifications };
updatedNotifications[dydxAddress] = notifications;
setAllTransferNotifications(updatedNotifications);
},
[setAllTransferNotifications, dydxAddress]
);
const addTransferNotification = useCallback(
(notification: TransferNotifcation) =>
setTransferNotifications([...transferNotifications, notification]),
[transferNotifications]
);
const squid = useSquid();
const { data: transferStatuses } = useQuery({
queryKey: ['getTransactionStatus', transferNotifications],
queryFn: async () => {
const statuses: { [key: string]: StatusResponse } = {};
for (const {
txHash,
toChainId,
fromChainId,
status: currentStatus,
} of transferNotifications) {
if (currentStatus && currentStatus?.squidTransactionStatus !== 'ongoing') continue;
const status = await squid?.getStatus({ transactionId: txHash, toChainId, fromChainId });
if (status) statuses[txHash] = status;
}
return statuses;
},
refetchInterval: TRANSFER_STATUS_FETCH_INTERVAL,
});
useEffect(() => {
if (!transferStatuses) return;
const newTransferNotifications = transferNotifications.map((notification) => {
const status = transferStatuses[notification.txHash];
if (!status) return notification;
return {
...notification,
status,
};
});
setTransferNotifications(newTransferNotifications);
}, [transferStatuses]);
return {
transferNotifications,
addTransferNotification,
};
};
+56 -1
View File
@@ -1,4 +1,4 @@
import { useEffect, useMemo } from 'react';
import { useCallback, useEffect, useMemo } from 'react';
import { useSelector, shallowEqual, useDispatch } from 'react-redux';
import { groupBy } from 'lodash';
@@ -8,12 +8,20 @@ import { STRING_KEYS } from '@/constants/localization';
import { type NotificationTypeConfig, NotificationType } from '@/constants/notifications';
import { ORDER_SIDE_STRINGS, TRADE_TYPE_STRINGS, TradeTypes } from '@/constants/trade';
import { useLocalNotifications } from '@/hooks/useLocalNotifications';
import { Icon, IconName } from '@/components/Icon';
import { Output, OutputType } from '@/components/Output';
import { TransferStatusToast } from '@/views/TransferStatus';
import { getSubaccountFills, getSubaccountOrders } from '@/state/accountSelectors';
import { openDialog } from '@/state/dialogs';
import { OrderStatusIcon } from '@/views/OrderStatusIcon';
import { useStringGetter } from './useStringGetter';
import { TransferStatusSteps } from '@/views/TransferStatusSteps';
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client';
export const notificationTypes = [
{
@@ -93,4 +101,51 @@ export const notificationTypes = [
};
},
} as NotificationTypeConfig<string, [string, number]>,
{
type: NotificationType.SquidTransfer,
useTrigger: ({ trigger }) => {
const stringGetter = useStringGetter();
const { transferNotifications } = useLocalNotifications();
const getTitleStringKey = useCallback((type: 'deposit' | 'withdraw', finished: boolean) => {
if (type === 'deposit' && !finished) return STRING_KEYS.DEPOSIT_IN_PROGRESS;
if (type === 'deposit' && finished) return STRING_KEYS.DEPOSIT;
if (type === 'withdraw' && !finished) return STRING_KEYS.WITHDRAW_IN_PROGRESS;
return STRING_KEYS.WITHDRAW;
}, []);
useEffect(() => {
for (const transfer of transferNotifications) {
const { toChainId, status, txHash, toAmount } = transfer;
const finished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing';
const type = toChainId === TESTNET_CHAIN_ID ? 'deposit' : 'withdraw';
trigger(
txHash,
{
icon: <Icon iconName={finished ? IconName.Transfer : IconName.Clock} />,
title: stringGetter({ key: getTitleStringKey(type, finished) }),
// TODO: confirm with design what the description should be
description: (
<>
<span>{type === 'deposit' ? 'Deposit of ' : 'Withdraw of'}</span>
<Output type={OutputType.Fiat} value={toAmount} />
</>
),
customContent: (
<TransferStatusToast
toAmount={transfer.toAmount}
triggeredAt={transfer.triggeredAt}
status={transfer.status}
/>
),
customMenuContent: !finished && <TransferStatusSteps status={transfer.status} />,
toastSensitivity: 'foreground',
},
[]
);
}
}, [transferNotifications]);
},
},
] satisfies NotificationTypeConfig[];
+7 -3
View File
@@ -106,7 +106,10 @@ const useNotificationsContext = () => {
updateKey,
} as Notification);
updateStatus(notification, isNew ? NotificationStatus.Triggered : NotificationStatus.Cleared);
updateStatus(
notification,
isNew ? NotificationStatus.Triggered : NotificationStatus.Cleared
);
}
// updateKey changed - update existing notification
@@ -141,7 +144,9 @@ const useNotificationsContext = () => {
// Push notifications
const [hasEnabledPush, setHasEnabledPush] = useLocalStorage({
key: LocalStorageKey.PushNotificationsEnabled,
defaultValue: Boolean(globalThis.Notification && globalThis.Notification.permission === 'granted'),
defaultValue: Boolean(
globalThis.Notification && globalThis.Notification.permission === 'granted'
),
});
const [isEnablingPush, setIsEnablingPush] = useState(false);
@@ -236,4 +241,3 @@ const useNotificationsContext = () => {
setIsMenuOpen,
};
};
// }
+1 -1
View File
@@ -3,7 +3,7 @@ import { useQuery } from 'react-query';
import { useAccounts } from '@/hooks';
import { DydxAddress } from '@/constants/wallets';
import { DYDX_DENOM } from '@dydxprotocol/v4-client-js';
import { DYDX_DENOM } from '@dydxprotocol/v4-client';
const ACCOUNT_BALANCE_POLLING_INTERVAL = 60_000;
+2 -2
View File
@@ -1,8 +1,8 @@
import { useCallback } from 'react';
import { useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { LocalStorageKey } from '@/constants/localStorage';
import { DEFAULT_APP_ENVIRONMENT, DydxNetwork } from '@/constants/networks';
import { DEFAULT_APP_ENVIRONMENT, DydxNetwork, DydxV4Network } from '@/constants/networks';
import { useAccounts, useLocalStorage } from '@/hooks';
+1 -1
View File
@@ -17,7 +17,7 @@ import {
DYDX_DENOM,
USDC_DENOM,
GAS_PRICE_DYDX_DENOM,
} from '@dydxprotocol/v4-client-js';
} from '@dydxprotocol/v4-client';
import type {
HumanReadableCancelOrderPayload,
+1
View File
@@ -41,6 +41,7 @@ export const NotificationsToastArea = ({ className }: StyleProps) => {
slotIcon={displayData.icon}
slotTitle={displayData.title}
slotDescription={displayData.description}
slotCustomContent={displayData.customContent}
slotAction={
<Button size={ButtonSize.Small} onClick={() => onNotificationAction(notification)}>
{displayData.actionDescription}
+1 -1
View File
@@ -1,4 +1,4 @@
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { OrderSide } from '@dydxprotocol/v4-client';
import {
AbacusOrderSide,
+1 -1
View File
@@ -8,7 +8,7 @@ import {
Network,
NetworkOptimizer,
ValidatorConfig,
} from '@dydxprotocol/v4-client-js';
} from '@dydxprotocol/v4-client';
import {
type AbacusDydxChainQueriesProtocol,
-23
View File
@@ -1,23 +0,0 @@
import type { AbacusFileSystemProtocol, FileLocation, Nullable } from '@/constants/abacus';
import { NETWORK_ENDPOINTS } from '@/constants/networks';
export const ENDPOINTS_PATH =
import.meta.env.MODE === 'production'
? 'config/prod/endpoints.json'
: 'config/staging/dev_endpoints.json';
class AbacusFileSystem implements AbacusFileSystemProtocol {
readTextFile(location: FileLocation, path: string): Nullable<string> {
if (path === ENDPOINTS_PATH) {
return JSON.stringify(NETWORK_ENDPOINTS);
}
return null;
}
writeTextFile(path: string, text: string): boolean {
return true;
}
}
export default AbacusFileSystem;
+2 -4
View File
@@ -36,8 +36,8 @@ import AbacusStateNotifier from './stateNotification';
import AbacusLocalizer from './localizer';
import AbacusFormatter from './formatter';
import AbacusThreading from './threading';
import AbacusFileSystem, { ENDPOINTS_PATH } from './filesystem';
import { LocaleSeparators } from '../numbers';
class AbacusStateManager {
private store: RootStore | undefined;
private currentMarket: string | undefined;
@@ -62,7 +62,7 @@ class AbacusStateManager {
null,
new AbacusThreading(),
new CoroutineTimer(),
new AbacusFileSystem()
null
);
const uiImplementations = new UIImplementations(
@@ -72,8 +72,6 @@ class AbacusStateManager {
);
this.stateManager = new AsyncAbacusStateManager(
import.meta.env.SHARED_RESOURCES_URI,
ENDPOINTS_PATH,
ioImplementations,
uiImplementations,
// @ts-ignore
-11
View File
@@ -1,8 +1,5 @@
import { kollections } from '@dydxprotocol/abacus';
import type {
AbacusApiState,
AbacusNotification,
AbacusStateNotificationProtocol,
Asset,
Nullable,
@@ -39,14 +36,6 @@ class AbacusStateNotifier implements AbacusStateNotificationProtocol {
this.store = undefined;
}
environmentsChanged(): void {
return;
}
notificationsChanged(notifications: kollections.List<AbacusNotification>): void {
return;
}
stateChanged(
updatedState: Nullable<PerpetualState>,
incomingChanges: Nullable<PerpetualStateChanges>
+6
View File
@@ -72,3 +72,9 @@ export const getTimeTillNextUnit = (unit: 'minute' | 'hour' | 'day') => {
};
export const getTimeString = (time: number) => time.toString().padStart(2, '0');
export const formatSeconds = (seconds: number) => {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
return `${getTimeString(minutes)}:${getTimeString(remainingSeconds)}`;
}
+1 -1
View File
@@ -1,5 +1,5 @@
import { type Location, matchPath } from 'react-router-dom';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { OrderSide } from '@dydxprotocol/v4-client';
import { StringGetterFunction } from '@/constants/localization';
+10 -17
View File
@@ -113,6 +113,7 @@
"HEDGIES": "Hedgies",
"HIDE_SECTION": "Hide section",
"HIDE": "Hide",
"HIDE_DETAILS": "Hide details",
"HISTORY": "History",
"INCREASED": "increased",
"INCREASING": "Increasing",
@@ -388,6 +389,7 @@
"CONNECTING_SUBTITLE": "Check your wallet for a connection request. Connecting to dYdX is free and does not affect your funds.",
"COULD_NOT_FIND_AFFILIATE": "We couldn't find this affiliate, please check the link.",
"CREATE_OR_VERIFY_COSMOS_ADDRESS": "Create or verify Cosmos address.",
"DEPOSIT_STATUS": "Your deposit of {AMOUNT_USD} will be available in approximately {ESTIMATED_DURATION}.",
"DONT_MISS": "Don't miss your chance to get a {DISCOUNT} on trading fees!",
"ENABLE_API": "Enable secure access to our API for lightning quick trading",
"ENABLE_TRADING": "Enable trading",
@@ -450,6 +452,7 @@
"VERIFY_WALLET_COMPATIBILITY": "Verify wallet compatibility",
"WELCOME_DYDX": "Welcome to dYdX",
"WELCOME": "Welcome",
"WITHDRAW_STATUS": "Your withdrawal of {AMOUNT_USD} will be available in approximately {ESTIMATED_DURATION}.",
"WRONG_NETWORK_EXPLANATION_PRODUCTION": "To complete dYdX onboarding, set your wallet network to 'Ethereum Mainnet'.",
"WRONG_NETWORK_EXPLANATION_STAGING": "To complete dYdX onboarding, set your wallet network to 'Goerli Test Network'.",
"WRONG_NETWORK": "Wrong network",
@@ -915,13 +918,15 @@
"66_REDUCTION": "66% reduction",
"ADJUSTED_IMR": "Adjusted Margin",
"ADJUSTED_IMR_BODY": "We have temporarily {ACTION} the Initial Margin Requirements on {MARKET} to {PERCENT} due to volatility. {ADDITIONAL_NOTE}",
"BRIDGING_TOKENS": "Bridging tokens",
"CLOSE_MARKET_POSITIONS": "Close your {MARKET} position",
"CLOSE_MARKET_POSITIONS_BODY": "{MARKET} is set to close only mode and the close price of {MARKET} is now fixed. Please close your open {MARKET} position as soon as you are able. We're here to help via the help chat if you run into issues.",
"COMPLIANCE_ALERT": "Compliance alert",
"DEPOSIT_IN_PROGRESS_DESCRIPTION": "Your deposit of {AMOUNT_ELEMENT} will be available after 14 confirmations.",
"DEPOSIT_IN_PROGRESS": "Deposit in progress",
"DEPOSIT_IN_PROGRESS": "Deposit in progress...",
"DEPOSIT_SUCCESS_DESCRIPTION": "Your deposit of {AMOUNT_ELEMENT} has been confirmed and is now available for trading.",
"DEPOSIT_SUCCESS": "Deposit success!",
"DEPOSIT_TO_CHAIN": "Deposit to {CHAIN}",
"DIRECT_TRANSFER_SUCCESS": "Direct transfer success!",
"DIRECT_TRANSFER_SUCCESS_DESCRIPTION": "Your direct transfer of {AMOUNT_ELEMENT} has been confirmed.",
"EPOCH_REWARDS": "Epoch {EPOCH_NUMBER} Rewards",
@@ -930,6 +935,8 @@
"FAST_WITHDRAW_PENDING": "Fast withdraw pending",
"FAST_WITHDRAW_SUCCESS_DESCRIPTION": "Your fast withdraw of {AMOUNT_ELEMENT} has been confirmed.",
"FAST_WITHDRAW_SUCCESS": "Fast withdraw success!",
"INITIATED_DEPOSIT": "Initiated deposit",
"INITIATED_WITHDRAWAL": "Initiated withdrawal",
"LEGAL_UPDATES": "Legal updates",
"LEGAL_UPDATES_DESCRIPTION": "Please check out our updated {TOU} and {PRIVACY_POLICY}.",
"MAINTENANCE_MARGIN_ADJUSTMENTS": "Maintenance margin requirements {ACTION} at {PERCENT}%.",
@@ -946,7 +953,8 @@
"SLOW_WITHDRAW_PENDING": "Slow withdraw(s) pending",
"SUSPICIOUS_TRADE": "Suspicious activity",
"SUSPICIOUS_TRADE_BODY": "We have noticed suspicious trading activity related to your account on dYdX, including potential wash trading activity. We will be reviewing your activity on an ongoing basis. If we notice similar activity in the future, then you will be permanently blocked from performing transfers within the protocol and from placing orders other than market orders that reduce your positions.",
"WITHDRAW_IN_PROGRESS": "Withdrawal(s) in progress"
"WITHDRAW_IN_PROGRESS": "Withdrawal(s) in progress...",
"WITHDRAW_TO_CHAIN": "Withdraw to {CHAIN}"
},
"EMAIL_NOTIFICATIONS": {
"ACCOUNT_DESCRIPTION": "Deposits, Withdrawals, Account Updates",
@@ -1209,20 +1217,5 @@
"NODE_DOWN": "Node cannot be reached.",
"NODE_HALTED": "Node has halted on block {BLOCK_NUMBER}.",
"UNKNOWN_API_ERROR": "Unknown API error"
},
"CHAIN": {
"V3_MAINNET": "V3 Mainnet",
"V3_GOERLI": "V3 Staging",
"V4_DEVNET": "V4 Dev 1",
"V4_DEVNET_2": "V4 Dev 2",
"V4_DEVNET_3": "V4 Dev 3",
"V4_DEVNET_4": "V4 Dev 4",
"V4_DEVNET_5": "V4 Dev 5",
"V4_STAGING": "V4 Staging",
"V4_TESTNET2": "V4 Testnet",
"V4_TESTNET2_DSRV": "V4 Testnet (DSRV)",
"V4_TESTNET2_DYDX": "V4 Testnet (dYdX)",
"V4_TESTNET2_NODEFLEET": "V4 Testnet (nodefleet)",
"V4_TESTNET2_IMPERATOR": "V4 Testnet (Imperator)"
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
import { createSelector } from 'reselect';
import { SubaccountPosition } from '@/constants/abacus';
import { OnboardingState, OnboardingSteps } from '@/constants/account';
import { OnboardingGuard, OnboardingState, OnboardingSteps } from '@/constants/account';
import { isDydxV4Network } from '@/constants/networks';
import {
+1 -1
View File
@@ -1,4 +1,4 @@
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { OrderSide } from '@dydxprotocol/v4-client';
import { createSelector } from 'reselect';
import {
+2 -2
View File
@@ -1,7 +1,7 @@
import { useState, useCallback } from 'react';
import { useInterval } from '@/hooks';
import { getTimeTillNextUnit, getTimeString } from '@/lib/timeUtils';
import { getTimeTillNextUnit, formatSeconds } from '@/lib/timeUtils';
import { Output, OutputType } from '@/components/Output';
export const NextFundingTimer = () => {
@@ -18,7 +18,7 @@ export const NextFundingTimer = () => {
type={OutputType.Text}
value={
secondsLeft !== undefined
? `${getTimeString(Math.floor(secondsLeft / 60))}:${getTimeString(secondsLeft % 60)}`
? formatSeconds(secondsLeft)
: undefined
}
/>
+156
View File
@@ -0,0 +1,156 @@
import { useCallback, useState, useMemo } from 'react';
import styled, { type AnyStyledComponent } from 'styled-components';
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client';
import { Root, Trigger, Content } from '@radix-ui/react-collapsible';
import { StatusResponse } from '@0xsquid/sdk';
import { useInterval, useStringGetter } from '@/hooks';
import { STRING_KEYS } from '@/constants/localization';
import { formatSeconds } from '@/lib/timeUtils';
import { Output, OutputType } from '@/components/Output';
import { WithReceipt } from '@/components/WithReceipt';
import { Icon, IconName } from '@/components/Icon';
import { TransferStatusSteps } from '@/views/TransferStatusSteps';
import { LoadingDots } from '@/components/Loading/LoadingDots';
import { layoutMixins } from '@/styles/layoutMixins';
type ElementProps = {
toAmount?: number;
triggeredAt?: number;
status?: StatusResponse;
};
export const TransferStatusToast = ({
toAmount,
triggeredAt = Date.now(),
status,
}: ElementProps) => {
const stringGetter = useStringGetter();
const [open, setOpen] = useState<boolean>(false);
const [secondsLeft, setSecondsLeft] = useState<number | undefined>();
const type = useMemo(
() => (status?.toChain?.chainData.chainId === TESTNET_CHAIN_ID ? 'deposit' : 'withdrawal'),
[status]
);
const updateSecondsLeft = useCallback(() => {
const fromChainEta = (status?.fromChain?.chainData?.estimatedRouteDuration || 0) * 1000;
const toChainEta = (status?.toChain?.chainData?.estimatedRouteDuration || 0) * 1000;
setSecondsLeft(Math.floor((triggeredAt + fromChainEta + toChainEta - Date.now()) / 1000));
}, [status]);
useInterval({ callback: updateSecondsLeft });
if (!status) return <LoadingDots size={3} />;
return (
<Styled.Root open={open} onOpenChange={setOpen}>
<WithReceipt
hideReceipt={!open}
side="bottom"
slotReceipt={
<Styled.Receipt>
<TransferStatusSteps status={status} />
</Styled.Receipt>
}
>
<Styled.BridgingStatus>
<Styled.Status>
{stringGetter({
key: type === 'deposit' ? STRING_KEYS.DEPOSIT_STATUS : STRING_KEYS.WITHDRAW_STATUS,
params: {
AMOUNT_USD: <Styled.InlineOutput type={OutputType.Fiat} value={toAmount} />,
ESTIMATED_DURATION: (
<Styled.InlineOutput
type={OutputType.Text}
value={formatSeconds(Math.max(secondsLeft || 0, 0))}
/>
),
},
})}
</Styled.Status>
<Styled.Trigger>
<Styled.TriggerIcon>
<Icon iconName={IconName.Caret} />
</Styled.TriggerIcon>
{stringGetter({ key: open ? STRING_KEYS.HIDE_DETAILS : STRING_KEYS.VIEW_DETAILS })}
</Styled.Trigger>
</Styled.BridgingStatus>
</WithReceipt>
</Styled.Root>
);
};
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Root = styled(Root)`
margin: 0.5rem -1rem -1rem -1rem;
color: var(--color-text-0);
font: var(--font-small-book);
`;
Styled.Receipt = styled.div`
padding: 0 1rem;
`;
Styled.BridgingStatus = styled.div`
${layoutMixins.flexColumn};
background-color: var(--color-layer-3);
gap: 0.5rem;
padding: 0 1rem 1rem 1rem;
border-radius: 0.5rem;
`;
Styled.Status = styled.div`
color: var(--color-text-0);
font-size: 0.875rem;
`;
Styled.InlineOutput = styled(Output)`
display: inline-block;
color: var(--color-text-1);
`;
Styled.Content = styled(Content)`
${layoutMixins.flexColumn};
gap: 0.5rem;
padding: 1rem;
`;
Styled.Step = styled.div`
${layoutMixins.row};
gap: 0.5rem;
`;
Styled.Trigger = styled(Trigger)`
display: flex;
align-items: center;
gap: 0.5em;
color: var(--color-accent);
user-select: none;
&:focus {
outline: none;
}
`;
Styled.TriggerIcon = styled.span`
width: 0.75em;
display: inline-flex;
transition: transform 0.3s var(--ease-out-expo);
${Styled.Trigger}[data-state='open'] & {
rotate: -0.5turn;
}
`;
+178
View File
@@ -0,0 +1,178 @@
import { useMemo } from 'react';
import styled, { css, keyframes, type AnyStyledComponent } from 'styled-components';
import { StatusResponse } from '@0xsquid/sdk';
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client';
import { useStringGetter } from '@/hooks';
import { Link } from '@/components/Link';
import { Icon, IconName } from '@/components/Icon';
import { LoadingDots } from '@/components/Loading/LoadingDots';
import { LoadingSpinner } from '@/components/Loading/LoadingSpinner';
import { layoutMixins } from '@/styles/layoutMixins';
import { STRING_KEYS } from '@/constants/localization';
type ElementProps = {
status?: StatusResponse;
};
enum TransferStatusStep {
FromChain,
Bridge,
ToChain,
Complete,
}
export const TransferStatusSteps = ({ status }: ElementProps) => {
const stringGetter = useStringGetter();
const type = useMemo(
() => (status?.toChain?.chainData.chainId === TESTNET_CHAIN_ID ? 'deposit' : 'withdrawal'),
[status]
);
const currentStep = useMemo(() => {
const routeStatus = status?.routeStatus;
const fromChain = status?.fromChain?.chainData.chainId;
const toChain = status?.toChain?.chainData.chainId;
if (!routeStatus?.length) return TransferStatusStep.FromChain;
const currentStatus = routeStatus[routeStatus?.length - 1];
if (currentStatus.chainId === toChain) {
return currentStatus.status !== 'success'
? TransferStatusStep.ToChain
: TransferStatusStep.Complete;
}
if (currentStatus.chainId === fromChain && currentStatus.status !== 'success') {
return TransferStatusStep.FromChain;
}
return TransferStatusStep.Bridge;
}, [status]);
const steps = useMemo(
() => [
{
label: stringGetter({
key: type === 'deposit' ? STRING_KEYS.INITIATED_DEPOSIT : STRING_KEYS.INITIATED_WITHDRAWAL,
}),
step: TransferStatusStep.FromChain,
link: status?.fromChain?.transactionUrl,
},
{
label: stringGetter({ key: STRING_KEYS.BRIDGING_TOKENS }),
step: TransferStatusStep.Bridge,
link: status?.axelarTransactionUrl,
},
{
label: stringGetter({
key: type === 'deposit' ? STRING_KEYS.DEPOSIT_TO_CHAIN : STRING_KEYS.WITHDRAW_TO_CHAIN,
params: {
CHAIN: status?.toChain?.chainData.chainName,
}
}),
step: TransferStatusStep.ToChain,
link: status?.toChain?.transactionUrl,
},
],
[status]
);
if (!status) return <LoadingDots size={3} />;
return (
<Styled.BridgingStatus>
{steps.map((step) => (
<Styled.Step key={step.step}>
<Styled.row>
{step.step === currentStep ? (
<Styled.Icon>
<Styled.Spinner />
</Styled.Icon>
) : step.step < currentStep ? (
<Styled.Icon state="complete">
<Icon iconName={IconName.Check} />
</Styled.Icon>
) : (
<Styled.Icon state="default">{step.step + 1}</Styled.Icon>
)}
{step.link && currentStep >= step.step ? (
<Link href={step.link}>
<Styled.Label highlighted={currentStep >= step.step}>
{step.label}
<Icon iconName={IconName.LinkOut} />
</Styled.Label>
</Link>
) : (
<Styled.Label highlighted={currentStep >= step.step}>
{step.label}
</Styled.Label>
)}
</Styled.row>
</Styled.Step>
))}
</Styled.BridgingStatus>
);
};
const Styled: Record<string, AnyStyledComponent> = {};
Styled.BridgingStatus = styled.div`
${layoutMixins.flexColumn};
gap: 1rem;
padding: 1rem 0;
`;
Styled.Step = styled.div`
${layoutMixins.spacedRow};
`;
Styled.row = styled.div`
${layoutMixins.inlineRow};
gap: 0.5rem;
`;
Styled.Icon = styled.div<{ state: 'complete' | 'default' }>`
display: flex;
align-items: center;
justify-content: center;
height: 2rem;
width: 2rem;
border-radius: 50%;
background-color: var(--color-layer-3);
${({ state }) =>
({
['complete']: css`
color: var(--color-positive);
`,
['default']: css`
color: var(--color-text-0);
`,
}[state])}
`;
Styled.Spinner = styled(LoadingSpinner)`
--spinner-width: 1.25rem;
color: var(--color-accent);
`;
Styled.Label = styled(Styled.row)<{ highlighted?: boolean }>`
${({ highlighted }) =>
highlighted
? css`
color: var(--color-text-2);
`
: css`
color: var(--color-text-0);
`}
`;
+1 -1
View File
@@ -33,7 +33,7 @@ import { Details } from '@/components/Details';
import { LoadingSpace } from '@/components/Loading/LoadingSpinner';
import { Output, OutputType } from '@/components/Output';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { OrderSide } from '@dydxprotocol/v4-client';
// @ts-ignore
const theme = buildChartTheme({
+2 -2
View File
@@ -3,7 +3,7 @@ import styled, { AnyStyledComponent } from 'styled-components';
import { useDispatch, useSelector } from 'react-redux';
import { AbacusApiStatus } from '@/constants/abacus';
import { CLIENT_NETWORK_CONFIGS } from '@/constants/networks';
import { DydxV4Network } from '@/constants/networks';
import { UNICODE } from '@/constants/unicode';
import { STRING_KEYS } from '@/constants/localization';
@@ -44,7 +44,7 @@ export const ExchangeOfflineDialog = ({ preventClose, setIsOpen }: ElementProps)
>
<Styled.Content>
<p>{statusErrorMessage}</p>
{CLIENT_NETWORK_CONFIGS[selectedNetwork].dydxChainId === 'dydx-testnet-2' && (
{selectedNetwork === DydxV4Network.V4Testnet2 && (
<Styled.Link href="https://status.v4testnet.dydx.exchange">
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} {UNICODE.ARROW_RIGHT}
</Styled.Link>
@@ -1,7 +1,7 @@
import { useState } from 'react';
import { useSignTypedData } from 'wagmi';
import styled, { type AnyStyledComponent, css } from 'styled-components';
import { useSelector } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { AES } from 'crypto-js';
import { EvmDerivedAccountStatus } from '@/constants/account';
@@ -9,7 +9,7 @@ import { AlertType } from '@/constants/alerts';
import { AnalyticsEvent } from '@/constants/analytics';
import { ButtonAction } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { CLIENT_NETWORK_CONFIGS } from '@/constants/networks';
import { CLIENT_NETWORK_CONFIGS, DydxV4Network } from '@/constants/networks';
import { DydxAddress, SIGN_TYPED_DATA } from '@/constants/wallets';
import { useAccounts, useBreakpoints, useDydxClient, useStringGetter } from '@/hooks';
@@ -54,7 +54,7 @@ export const GenerateKeys = ({
// 1. Switch network
const selectedNetwork = useSelector(getSelectedNetwork);
const chainId = Number(CLIENT_NETWORK_CONFIGS[selectedNetwork].ethereumChainId);
const chainId = Number(CLIENT_NETWORK_CONFIGS[selectedNetwork as DydxV4Network].ethereumChainId);
const { isMatchingNetwork, matchNetwork, isSwitchingNetwork } = useMatchingEvmNetwork({
chainId,
@@ -2,7 +2,7 @@ import { type FormEvent, useCallback, useEffect, useMemo, useState } from 'react
import styled, { type AnyStyledComponent } from 'styled-components';
import { type NumberFormatValues } from 'react-number-format';
import { shallowEqual, useSelector } from 'react-redux';
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client-js';
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client';
import { ethers } from 'ethers';
import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus';
@@ -13,6 +13,7 @@ import { NumberSign } from '@/constants/numbers';
import { useAccounts, useDebounce, useStringGetter } from '@/hooks';
import { useAccountBalance } from '@/hooks/useAccountBalance';
import { useLocalNotifications } from '@/hooks/useLocalNotifications';
import { layoutMixins } from '@/styles/layoutMixins';
import { formMixins } from '@/styles/formMixins';
@@ -50,11 +51,14 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
const { signerWagmi } = useAccounts();
const { addTransferNotification } = useLocalNotifications();
const {
requestPayload,
token,
chain: chainIdStr,
resources,
summary,
} = useSelector(getTransferInputs, shallowEqual) || {};
const chainId = chainIdStr ? parseInt(chainIdStr) : undefined;
@@ -69,8 +73,6 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
const debouncedAmount = useDebounce<string>(fromAmount, 500);
// Async Data
const [transactionHash, setTransactionHash] = useState<string>();
const { balance, queryStatus, isQueryFetching } = useAccountBalance({
addressOrDenom: sourceToken?.address || undefined,
assetSymbol: sourceToken?.symbol || undefined,
@@ -189,11 +191,12 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
onDeposit?.();
if (txHash) {
setTransactionHash(txHash);
abacusStateManager.setTransferStatus({
hash: txHash,
addTransferNotification({
txHash,
toChainId: TESTNET_CHAIN_ID,
fromChainId: chainId?.toString(),
fromChainId: chainIdStr || undefined,
toAmount: summary?.usdcSize || undefined,
triggeredAt: Date.now(),
});
abacusStateManager.clearTransferInputValues();
setFromAmount('');
@@ -286,17 +289,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
}
/>
</Styled.WithDetailsReceipt>
{errorMessage ? (
<AlertMessage type={AlertType.Error}>{errorMessage}</AlertMessage>
) : (
transactionHash && (
<AlertMessage type={AlertType.Success}>
<Styled.TransactionInfo>
{stringGetter({ key: STRING_KEYS.DEPOSIT_IN_PROGRESS })}
</Styled.TransactionInfo>
</AlertMessage>
)
)}
{errorMessage && <AlertMessage type={AlertType.Error}>{errorMessage}</AlertMessage>}
<DepositButtonAndReceipt
isDisabled={isDisabled}
isLoading={isLoading}
@@ -3,18 +3,18 @@ import type { ChangeEvent, FormEvent } from 'react';
import styled, { type AnyStyledComponent } from 'styled-components';
import type { NumberFormatValues } from 'react-number-format';
import { shallowEqual, useSelector } from 'react-redux';
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client-js';
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client';
import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus';
import { AlertType } from '@/constants/alerts';
import { ButtonSize } from '@/constants/buttons';
import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { NumberSign, QUANTUM_MULTIPLIER } from '@/constants/numbers';
import { useDebounce, useStringGetter, useSubaccount } from '@/hooks';
import { useLocalNotifications } from '@/hooks/useLocalNotifications';
import { layoutMixins } from '@/styles/layoutMixins';
import { formMixins } from '@/styles/formMixins';
import { AlertMessage } from '@/components/AlertMessage';
import { Button } from '@/components/Button';
@@ -63,9 +63,9 @@ export const WithdrawForm = () => {
[token, resources]
);
// Async Data
const [transactionHash, setTransactionHash] = useState<string>();
const { addTransferNotification } = useLocalNotifications();
// Async Data
const debouncedAmountBN = MustBigNumber(debouncedAmount);
const withdrawAmountBN = MustBigNumber(withdrawAmount);
const freeCollateralBN = MustBigNumber(freeCollateral?.current);
@@ -134,10 +134,13 @@ export const WithdrawForm = () => {
const hash = `0x${Buffer.from(txHash.hash).toString('hex')}`;
setTransactionHash(hash);
abacusStateManager.setTransferStatus({
hash,
addTransferNotification({
txHash: hash,
fromChainId: TESTNET_CHAIN_ID,
toChainId: chainIdStr || undefined,
toAmount: debouncedAmountBN.toNumber(),
triggeredAt: Date.now(),
});
abacusStateManager.clearTransferInputValues();
setWithdrawAmount('');
@@ -285,23 +288,18 @@ export const WithdrawForm = () => {
value={withdrawAmount}
label={stringGetter({ key: STRING_KEYS.AMOUNT })}
slotRight={
<Styled.FormInputButton size={ButtonSize.XSmall} onClick={onClickMax}>
<Button
type={ButtonType.Button}
action={ButtonAction.Secondary}
size={ButtonSize.XSmall}
onClick={onClickMax}
>
{stringGetter({ key: STRING_KEYS.MAX })}
</Styled.FormInputButton>
</Button>
}
/>
</Styled.WithDetailsReceipt>
{errorMessage ? (
<AlertMessage type={AlertType.Error}>{errorMessage}</AlertMessage>
) : (
transactionHash && (
<AlertMessage type={AlertType.Success}>
<Styled.TransactionInfo>
{stringGetter({ key: STRING_KEYS.WITHDRAW_IN_PROGRESS })}
</Styled.TransactionInfo>
</AlertMessage>
)
)}
{errorMessage && <AlertMessage type={AlertType.Error}>{errorMessage}</AlertMessage>}
<WithdrawButtonAndReceipt
isDisabled={isDisabled}
isLoading={isLoading}
@@ -352,7 +350,3 @@ Styled.Link = styled(Link)`
Styled.TransactionInfo = styled.span`
${layoutMixins.row}
`;
Styled.FormInputButton = styled(Button)`
${formMixins.inputInnerButton}
`;
@@ -19,11 +19,11 @@ import { Output, OutputType } from '@/components/Output';
import { ToggleButton } from '@/components/ToggleButton';
import { WithReceipt } from '@/components/WithReceipt';
import { SlippageEditor } from '../SlippageEditor';
import { getSubaccount } from '@/state/accountSelectors';
import { getTransferInputs } from '@/state/inputsSelectors';
import { SlippageEditor } from '../SlippageEditor';
type ElementProps = {
setSlippage: (slippage: number) => void;
@@ -1,7 +1,7 @@
import { useState } from 'react';
import type { Story } from '@ladle/react';
import styled, { AnyStyledComponent } from 'styled-components';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { OrderSide } from '@dydxprotocol/v4-client';
import { PositionSide } from '@/constants/trade';
import { breakpoints } from '@/styles';
+7 -3
View File
@@ -2,7 +2,7 @@ import { type Dispatch, type SetStateAction, useCallback, useMemo } from 'react'
import styled, { AnyStyledComponent, css } from 'styled-components';
import { Root, Thumb, Track } from '@radix-ui/react-slider';
import _ from 'lodash';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { OrderSide } from '@dydxprotocol/v4-client';
import { type Nullable, TradeInputField } from '@/constants/abacus';
import { PositionSide } from '@/constants/trade';
@@ -43,7 +43,9 @@ export const LeverageSlider = ({
},
[PositionSide.Long]: {
min:
orderSide === OrderSide.BUY ? leverageBN.toNumber() : maxLeverageBN.negated().toNumber(),
orderSide === OrderSide.BUY
? leverageBN.toNumber()
: maxLeverageBN.negated().toNumber(),
max: orderSide === OrderSide.BUY ? maxLeverageBN.toNumber() : leverageBN.toNumber(),
midpoint:
orderSide === OrderSide.SELL
@@ -54,7 +56,9 @@ export const LeverageSlider = ({
},
[PositionSide.Short]: {
min:
orderSide === OrderSide.BUY ? leverageBN.toNumber() : maxLeverageBN.negated().toNumber(),
orderSide === OrderSide.BUY
? leverageBN.toNumber()
: maxLeverageBN.negated().toNumber(),
max: orderSide === OrderSide.BUY ? maxLeverageBN.toNumber() : leverageBN.toNumber(),
midpoint:
orderSide === OrderSide.BUY
@@ -1,7 +1,7 @@
import type { Dispatch, SetStateAction } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import styled, { AnyStyledComponent } from 'styled-components';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { OrderSide } from '@dydxprotocol/v4-client';
import { TradeInputField, Nullable } from '@/constants/abacus';
import { ButtonShape } from '@/constants/buttons';
@@ -1,5 +1,5 @@
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { OrderSide } from '@dydxprotocol/v4-client';
import type { TradeInputSummary } from '@/constants/abacus';
import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
@@ -1,7 +1,7 @@
import { memo } from 'react';
import styled, { css } from 'styled-components';
import { shallowEqual, useSelector } from 'react-redux';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { OrderSide } from '@dydxprotocol/v4-client';
import { AbacusOrderSide } from '@/constants/abacus';
import { ButtonShape, ButtonSize } from '@/constants/buttons';
@@ -31,7 +31,9 @@ export const TradeSideToggle = memo(() => {
onValueChange={(side: OrderSide) => {
abacusStateManager.setTradeValue({
value:
side === OrderSide.BUY ? AbacusOrderSide.buy.rawValue : AbacusOrderSide.sell.rawValue,
side === OrderSide.BUY
? AbacusOrderSide.buy.rawValue
: AbacusOrderSide.sell.rawValue,
field: TradeInputField.side,
});
}}
+4 -4
View File
@@ -5,14 +5,14 @@ import { shallowEqual, useSelector } from 'react-redux';
import type { SyntheticInputEvent } from 'react-number-format/types/types';
import { debounce } from 'lodash';
import { StdFee } from '@cosmjs/stargate';
import { validation } from '@dydxprotocol/v4-client-js';
import { validation } from '@dydxprotocol/v4-client';
import { TransferInputField, TransferType } from '@/constants/abacus';
import { AlertType } from '@/constants/alerts';
import { ButtonShape, ButtonSize } from '@/constants/buttons';
import { ERROR_STRING_KEYS } from '@/constants/localization/errors';
import { STRING_KEYS } from '@/constants/localization';
import { CLIENT_NETWORK_CONFIGS } from '@/constants/networks';
import { CHAIN_IDS } from '@/constants/networks';
import { NumberSign, QUANTUM_MULTIPLIER } from '@/constants/numbers';
import { DYDX_CHAIN_ASSET_COIN_DENOM, DYDX_CHAIN_INFO, DydxChainAsset } from '@/constants/wallets';
@@ -228,7 +228,7 @@ export const TransferForm = ({
const networkOptions = [
{
chainId: CLIENT_NETWORK_CONFIGS[selectedNetwork].dydxChainId,
chainId: CHAIN_IDS[selectedNetwork],
label: (
<Styled.InlineRow>
<AssetIcon symbol="DYDX" /> {stringGetter({ key: STRING_KEYS.DYDX_CHAIN })}
@@ -318,7 +318,7 @@ export const TransferForm = ({
/>
<Styled.NetworkSelectMenu
label={stringGetter({ key: STRING_KEYS.NETWORK })}
value={CLIENT_NETWORK_CONFIGS[selectedNetwork].dydxChainId}
value={CHAIN_IDS[selectedNetwork]}
slotTriggerAfter={null}
>
{networkOptions.map(({ chainId, label }) => (
+1 -8
View File
@@ -1,7 +1,6 @@
import styled, { type AnyStyledComponent } from 'styled-components';
import { useSelectedNetwork } from '@/hooks';
import { layoutMixins } from '@/styles/layoutMixins';
import { headerMixins } from '@/styles/headerMixins';
import { DropdownSelectMenu } from '@/components/DropdownSelectMenu';
@@ -19,6 +18,7 @@ export const NetworkSelectMenu = ({ align, sideOffset }: StyleProps) => {
return (
<Styled.DropdownSelectMenu
disabled={import.meta.env.MODE === 'production'}
items={networks}
value={selectedNetwork}
onValueChange={switchNetwork}
@@ -32,11 +32,4 @@ const Styled: Record<string, AnyStyledComponent> = {};
Styled.DropdownSelectMenu = styled(DropdownSelectMenu)`
${headerMixins.dropdownTrigger}
& > span:first-of-type {
${layoutMixins.textOverflow}
max-width: 5.625rem;
min-width: 0;
white-space: nowrap;
}
`;
+3 -3
View File
@@ -89,9 +89,9 @@ export const NotificationsMenu = ({
.map(({ notification, key, displayData }) => ({
value: key,
label: displayData.title ?? '',
description: displayData.description,
slotBefore: displayData.icon,
slotAfter: (
description: displayData.customMenuContent || displayData.description,
slotBefore: !displayData.customMenuContent && displayData.icon,
slotAfter: !displayData.customMenuContent && (
<>
<$Output
type={OutputType.RelativeTime}
+43 -11
View File
@@ -1,13 +1,45 @@
import { type MenuItem } from '@/constants/menus';
import { type DydxNetwork, NETWORK_ENDPOINTS } from '@/constants/networks';
import { useStringGetter } from '@/hooks';
import { type DydxNetwork, DydxV3Network, DydxV4Network } from '@/constants/networks';
export const useNetworks = (): MenuItem<DydxNetwork>[] => {
const stringGetter = useStringGetter();
return NETWORK_ENDPOINTS.environments.map(({ stringKey, environment }) => ({
key: environment,
label: stringGetter({ key: stringKey }),
value: environment,
}));
};
export const useNetworks = (): MenuItem<DydxNetwork>[] =>
import.meta.env.MODE === 'production'
? [
{
value: DydxV4Network.V4Testnet2,
label: 'Testnet',
slotBefore: <b>v4</b>,
},
]
: [
{
value: DydxV4Network.V4Mainnet,
label: 'Mainnet',
slotBefore: <b>v4</b>,
disabled: true,
},
{
value: DydxV4Network.V4Staging,
label: 'Staging',
slotBefore: <b>v4</b>,
},
{
value: DydxV4Network.V4Testnet2,
label: 'Testnet-2',
slotBefore: <b>v4</b>,
},
{
value: DydxV4Network.V4Local,
label: 'Dev',
slotBefore: <b>v4</b>,
},
{
value: DydxV3Network.V3Mainnet,
label: 'Mainnet',
slotBefore: <b>v3</b>,
},
{
value: DydxV3Network.V3Testnet,
label: 'Testnet',
slotBefore: <b>v3</b>,
},
];
+1 -1
View File
@@ -1,7 +1,7 @@
import { useEffect } from 'react';
import styled, { type AnyStyledComponent, css } from 'styled-components';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { OrderSide } from '@dydxprotocol/v4-client';
import type { ColumnSize } from '@react-types/table';
import { type Asset, type SubaccountFill } from '@/constants/abacus';
+1 -1
View File
@@ -1,7 +1,7 @@
import { useMemo } from 'react';
import styled, { type AnyStyledComponent, css, keyframes } from 'styled-components';
import { shallowEqual, useSelector } from 'react-redux';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { OrderSide } from '@dydxprotocol/v4-client';
import { MarketTrade } from '@/constants/abacus';
import { STRING_KEYS } from '@/constants/localization';
+1 -1
View File
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import styled, { type AnyStyledComponent, css, keyframes } from 'styled-components';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { OrderSide } from '@dydxprotocol/v4-client';
import { type OrderbookLine, TradeInputField } from '@/constants/abacus';
import { STRING_KEYS } from '@/constants/localization';
+1 -1
View File
@@ -2,7 +2,7 @@ import { useEffect } from 'react';
import styled, { css, type AnyStyledComponent } from 'styled-components';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { DateTime } from 'luxon';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { OrderSide } from '@dydxprotocol/v4-client';
import { ColumnSize } from '@react-types/table';
import type { Dispatch } from '@reduxjs/toolkit';