Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e792c92c9c |
+1
-1
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"packageManager": "pnpm@8.6.6",
|
||||
"dependencies": {
|
||||
"@0xsquid/sdk": "^1.10.0",
|
||||
"@0xsquid/sdk": "^1.7.2",
|
||||
"@cosmjs/amino": "^0.31.0",
|
||||
"@cosmjs/crypto": "^0.31.0",
|
||||
"@cosmjs/encoding": "^0.31.0",
|
||||
|
||||
Generated
+1752
-189
File diff suppressed because it is too large
Load Diff
+24
-27
@@ -13,9 +13,8 @@ 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/useSquid';
|
||||
import { SquidProvider } from '@/hooks/useSquidRouter';
|
||||
|
||||
import { GuardedMobileRoute } from '@/components/GuardedMobileRoute';
|
||||
|
||||
@@ -99,31 +98,29 @@ const Content = () => {
|
||||
);
|
||||
};
|
||||
|
||||
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 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 Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
|
||||
+10
-12
@@ -17,7 +17,6 @@ type ElementProps = {
|
||||
slotIcon?: React.ReactNode;
|
||||
slotTitle?: React.ReactNode;
|
||||
slotDescription?: React.ReactNode;
|
||||
slotCustomContent?: React.ReactNode;
|
||||
slotAction?: React.ReactNode;
|
||||
actionDescription?: string;
|
||||
actionAltText?: string;
|
||||
@@ -34,7 +33,6 @@ export const Toast = ({
|
||||
slotIcon,
|
||||
slotTitle,
|
||||
slotDescription,
|
||||
slotCustomContent,
|
||||
slotAction,
|
||||
actionDescription = '',
|
||||
actionAltText = actionDescription,
|
||||
@@ -80,13 +78,12 @@ export const Toast = ({
|
||||
|
||||
<$Title>{slotTitle}</$Title>
|
||||
</$Header>
|
||||
{!slotCustomContent && <$Description>{slotDescription}</$Description>}
|
||||
{slotCustomContent}
|
||||
{actionDescription && (
|
||||
<$Action asChild altText={actionAltText}>
|
||||
{slotAction}
|
||||
</$Action>
|
||||
)}
|
||||
|
||||
<$Description>{slotDescription}</$Description>
|
||||
|
||||
<$Action asChild altText={actionAltText}>
|
||||
{slotAction}
|
||||
</$Action>
|
||||
</$Container>
|
||||
</div>
|
||||
</$Root>
|
||||
@@ -121,7 +118,8 @@ const $Root = styled(Root)`
|
||||
align-items: end;
|
||||
transform-origin: left bottom;
|
||||
|
||||
animation: ${keyframes`
|
||||
animation:
|
||||
${keyframes`
|
||||
from {
|
||||
/* scale: 0; */
|
||||
grid-template-rows: 0fr; // height transition
|
||||
@@ -205,8 +203,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']) & {
|
||||
|
||||
@@ -12,7 +12,6 @@ export enum LocalStorageKey {
|
||||
NotificationsLastUpdated = 'dydx.NotificationsLastUpdated',
|
||||
PushNotificationsEnabled = 'dydx.PushNotificationsEnabled',
|
||||
PushNotificationsLastUpdated = 'dydx.PushNotificationsLastUpdated',
|
||||
TransferNotifications = 'dydx.TransferNotifications',
|
||||
|
||||
// UI State
|
||||
LastViewedMarket = 'dydx.LastViewedMarket',
|
||||
|
||||
@@ -109,7 +109,6 @@ 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',
|
||||
@@ -384,7 +383,6 @@ 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',
|
||||
@@ -447,7 +445,6 @@ 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',
|
||||
@@ -903,7 +900,6 @@ 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',
|
||||
@@ -911,7 +907,6 @@ 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',
|
||||
@@ -920,8 +915,6 @@ 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',
|
||||
@@ -939,7 +932,6 @@ 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',
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
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 */
|
||||
@@ -36,7 +30,7 @@ export type NotificationTypeConfig<
|
||||
* @param true (default): Notification initialized with status NotificationStatus.Triggered
|
||||
* @param false: Notification initialized with status NotificationStatus.Cleared
|
||||
*/
|
||||
isNew?: boolean
|
||||
isNew?: boolean,
|
||||
) => void;
|
||||
|
||||
lastUpdated: number;
|
||||
@@ -64,10 +58,7 @@ 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;
|
||||
@@ -83,11 +74,7 @@ export type NotificationDisplayData = {
|
||||
|
||||
title?: string;
|
||||
|
||||
description?: React.ReactNode;
|
||||
|
||||
customContent?: React.ReactNode;
|
||||
|
||||
customMenuContent?: React.ReactNode;
|
||||
description?: string;
|
||||
|
||||
actionDescription?: string;
|
||||
|
||||
@@ -114,13 +101,3 @@ export type NotificationDisplayData = {
|
||||
*/
|
||||
toastDuration?: number;
|
||||
};
|
||||
|
||||
// Notification types
|
||||
export type TransferNotifcation = {
|
||||
txHash: string;
|
||||
toChainId?: string;
|
||||
fromChainId?: string;
|
||||
toAmount?: number;
|
||||
triggeredAt?: number;
|
||||
status?: StatusResponse;
|
||||
};
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
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,
|
||||
};
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useSelector, shallowEqual, useDispatch } from 'react-redux';
|
||||
import { groupBy } from 'lodash';
|
||||
|
||||
@@ -8,20 +8,12 @@ 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 = [
|
||||
{
|
||||
@@ -101,51 +93,4 @@ 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[];
|
||||
|
||||
@@ -106,10 +106,7 @@ const useNotificationsContext = () => {
|
||||
updateKey,
|
||||
} as Notification);
|
||||
|
||||
updateStatus(
|
||||
notification,
|
||||
isNew ? NotificationStatus.Triggered : NotificationStatus.Cleared
|
||||
);
|
||||
updateStatus(notification, isNew ? NotificationStatus.Triggered : NotificationStatus.Cleared);
|
||||
}
|
||||
|
||||
// updateKey changed - update existing notification
|
||||
@@ -144,9 +141,7 @@ 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);
|
||||
|
||||
@@ -241,3 +236,4 @@ const useNotificationsContext = () => {
|
||||
setIsMenuOpen,
|
||||
};
|
||||
};
|
||||
// }
|
||||
|
||||
@@ -41,7 +41,6 @@ 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}
|
||||
|
||||
@@ -72,9 +72,3 @@ 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)}`;
|
||||
}
|
||||
|
||||
@@ -113,7 +113,6 @@
|
||||
"HEDGIES": "Hedgies",
|
||||
"HIDE_SECTION": "Hide section",
|
||||
"HIDE": "Hide",
|
||||
"HIDE_DETAILS": "Hide details",
|
||||
"HISTORY": "History",
|
||||
"INCREASED": "increased",
|
||||
"INCREASING": "Increasing",
|
||||
@@ -389,7 +388,6 @@
|
||||
"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",
|
||||
@@ -452,7 +450,6 @@
|
||||
"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",
|
||||
@@ -918,15 +915,13 @@
|
||||
"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",
|
||||
@@ -935,8 +930,6 @@
|
||||
"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}%.",
|
||||
@@ -953,8 +946,7 @@
|
||||
"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_TO_CHAIN": "Withdraw to {CHAIN}"
|
||||
"WITHDRAW_IN_PROGRESS": "Withdrawal(s) in progress"
|
||||
},
|
||||
"EMAIL_NOTIFICATIONS": {
|
||||
"ACCOUNT_DESCRIPTION": "Deposits, Withdrawals, Account Updates",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { useInterval } from '@/hooks';
|
||||
|
||||
import { getTimeTillNextUnit, formatSeconds } from '@/lib/timeUtils';
|
||||
import { getTimeTillNextUnit, getTimeString } 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
|
||||
? formatSeconds(secondsLeft)
|
||||
? `${getTimeString(Math.floor(secondsLeft / 60))}:${getTimeString(secondsLeft % 60)}`
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
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;
|
||||
}
|
||||
`;
|
||||
@@ -1,178 +0,0 @@
|
||||
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);
|
||||
`}
|
||||
`;
|
||||
@@ -5,7 +5,11 @@ import { shallowEqual, useSelector } from 'react-redux';
|
||||
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client';
|
||||
import { ethers } from 'ethers';
|
||||
|
||||
import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus';
|
||||
import {
|
||||
TransferInputField,
|
||||
TransferInputTokenResource,
|
||||
TransferType,
|
||||
} from '@/constants/abacus';
|
||||
import { AlertType } from '@/constants/alerts';
|
||||
import { ButtonSize } from '@/constants/buttons';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
@@ -13,7 +17,6 @@ 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';
|
||||
@@ -51,16 +54,8 @@ 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;
|
||||
const { requestPayload, token, chain: chainIdStr, resources } = useSelector(getTransferInputs, shallowEqual) || {};
|
||||
const chainId = chainIdStr ? parseInt(chainIdStr) : undefined
|
||||
|
||||
// User inputs
|
||||
const sourceToken = useMemo(
|
||||
@@ -73,6 +68,8 @@ 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,
|
||||
@@ -191,16 +188,16 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
||||
onDeposit?.();
|
||||
|
||||
if (txHash) {
|
||||
addTransferNotification({
|
||||
txHash,
|
||||
setTransactionHash(txHash);
|
||||
abacusStateManager.setTransferStatus({
|
||||
hash: txHash,
|
||||
toChainId: TESTNET_CHAIN_ID,
|
||||
fromChainId: chainIdStr || undefined,
|
||||
toAmount: summary?.usdcSize || undefined,
|
||||
triggeredAt: Date.now(),
|
||||
fromChainId: chainId?.toString(),
|
||||
});
|
||||
abacusStateManager.clearTransferInputValues();
|
||||
setFromAmount('');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
setError(error);
|
||||
} finally {
|
||||
@@ -289,7 +286,17 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
||||
}
|
||||
/>
|
||||
</Styled.WithDetailsReceipt>
|
||||
{errorMessage && <AlertMessage type={AlertType.Error}>{errorMessage}</AlertMessage>}
|
||||
{errorMessage ? (
|
||||
<AlertMessage type={AlertType.Error}>{errorMessage}</AlertMessage>
|
||||
) : (
|
||||
transactionHash && (
|
||||
<AlertMessage type={AlertType.Success}>
|
||||
<Styled.TransactionInfo>
|
||||
{stringGetter({ key: STRING_KEYS.DEPOSIT_IN_PROGRESS })}
|
||||
</Styled.TransactionInfo>
|
||||
</AlertMessage>
|
||||
)
|
||||
)}
|
||||
<DepositButtonAndReceipt
|
||||
isDisabled={isDisabled}
|
||||
isLoading={isLoading}
|
||||
|
||||
@@ -7,14 +7,14 @@ import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client';
|
||||
|
||||
import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus';
|
||||
import { AlertType } from '@/constants/alerts';
|
||||
import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
|
||||
import { ButtonSize } 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]
|
||||
);
|
||||
|
||||
const { addTransferNotification } = useLocalNotifications();
|
||||
|
||||
// Async Data
|
||||
const [transactionHash, setTransactionHash] = useState<string>();
|
||||
|
||||
const debouncedAmountBN = MustBigNumber(debouncedAmount);
|
||||
const withdrawAmountBN = MustBigNumber(withdrawAmount);
|
||||
const freeCollateralBN = MustBigNumber(freeCollateral?.current);
|
||||
@@ -134,13 +134,10 @@ export const WithdrawForm = () => {
|
||||
const hash = `0x${Buffer.from(txHash.hash).toString('hex')}`;
|
||||
|
||||
setTransactionHash(hash);
|
||||
|
||||
addTransferNotification({
|
||||
txHash: hash,
|
||||
abacusStateManager.setTransferStatus({
|
||||
hash,
|
||||
fromChainId: TESTNET_CHAIN_ID,
|
||||
toChainId: chainIdStr || undefined,
|
||||
toAmount: debouncedAmountBN.toNumber(),
|
||||
triggeredAt: Date.now(),
|
||||
});
|
||||
abacusStateManager.clearTransferInputValues();
|
||||
setWithdrawAmount('');
|
||||
@@ -288,18 +285,23 @@ export const WithdrawForm = () => {
|
||||
value={withdrawAmount}
|
||||
label={stringGetter({ key: STRING_KEYS.AMOUNT })}
|
||||
slotRight={
|
||||
<Button
|
||||
type={ButtonType.Button}
|
||||
action={ButtonAction.Secondary}
|
||||
size={ButtonSize.XSmall}
|
||||
onClick={onClickMax}
|
||||
>
|
||||
<Styled.FormInputButton size={ButtonSize.XSmall} onClick={onClickMax}>
|
||||
{stringGetter({ key: STRING_KEYS.MAX })}
|
||||
</Button>
|
||||
</Styled.FormInputButton>
|
||||
}
|
||||
/>
|
||||
</Styled.WithDetailsReceipt>
|
||||
{errorMessage && <AlertMessage type={AlertType.Error}>{errorMessage}</AlertMessage>}
|
||||
{errorMessage ? (
|
||||
<AlertMessage type={AlertType.Error}>{errorMessage}</AlertMessage>
|
||||
) : (
|
||||
transactionHash && (
|
||||
<AlertMessage type={AlertType.Success}>
|
||||
<Styled.TransactionInfo>
|
||||
{stringGetter({ key: STRING_KEYS.WITHDRAW_IN_PROGRESS })}
|
||||
</Styled.TransactionInfo>
|
||||
</AlertMessage>
|
||||
)
|
||||
)}
|
||||
<WithdrawButtonAndReceipt
|
||||
isDisabled={isDisabled}
|
||||
isLoading={isLoading}
|
||||
@@ -350,3 +352,7 @@ 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;
|
||||
|
||||
|
||||
@@ -89,9 +89,9 @@ export const NotificationsMenu = ({
|
||||
.map(({ notification, key, displayData }) => ({
|
||||
value: key,
|
||||
label: displayData.title ?? '',
|
||||
description: displayData.customMenuContent || displayData.description,
|
||||
slotBefore: !displayData.customMenuContent && displayData.icon,
|
||||
slotAfter: !displayData.customMenuContent && (
|
||||
description: displayData.description,
|
||||
slotBefore: displayData.icon,
|
||||
slotAfter: (
|
||||
<>
|
||||
<$Output
|
||||
type={OutputType.RelativeTime}
|
||||
|
||||
Reference in New Issue
Block a user