diff --git a/package.json b/package.json index 3bce753..1a28d72 100644 --- a/package.json +++ b/package.json @@ -38,8 +38,8 @@ "@cosmjs/stargate": "^0.31.0", "@cosmjs/tendermint-rpc": "^0.31.0", "@dydxprotocol/v4-abacus": "^0.4.28", - "@dydxprotocol/v4-client-js": "^0.35.0", - "@dydxprotocol/v4-localization": "^0.1.5", + "@dydxprotocol/v4-client-js": "^0.36.1", + "@dydxprotocol/v4-localization": "^0.1.8", "@ethersproject/providers": "^5.7.2", "@js-joda/core": "^5.5.3", "@radix-ui/react-collapsible": "^1.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ab8d322..c7f1e02 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,11 +30,11 @@ dependencies: specifier: ^0.4.28 version: 0.4.28 '@dydxprotocol/v4-client-js': - specifier: ^0.35.0 - version: 0.35.0 + specifier: ^0.36.1 + version: 0.36.1 '@dydxprotocol/v4-localization': - specifier: ^0.1.5 - version: 0.1.5 + specifier: ^0.1.8 + version: 0.1.8 '@ethersproject/providers': specifier: ^5.7.2 version: 5.7.2 @@ -983,8 +983,8 @@ packages: resolution: {integrity: sha512-RQGTXI7q4HAXDmlUpDhpJDLN8C0dFMgKHzFITK1Sz3KnLb3mkkpqEU1D8VIn/hB7ngt+equMsLFSkUwzXCzgdA==} dev: false - /@dydxprotocol/v4-client-js@0.35.0: - resolution: {integrity: sha512-H+0/tOBVqyWx2JQ4jUFdJOlFof0NqN1u9vzKJ2h3Nv5KNteSPd59eiywoz+3J0jlmkREw0Jzg9CS0bLwzYVLvA==} + /@dydxprotocol/v4-client-js@0.36.1: + resolution: {integrity: sha512-KJ3MtWI61wmm+xZtXQd7/hYKIoVFG32TRdyyVaXXO8vpW9Oqd0JD8QSM/wuegkciAh5YmP/BrDzYuu77zHO6yg==} dependencies: '@cosmjs/amino': 0.30.1 '@cosmjs/encoding': 0.31.1 @@ -1010,8 +1010,8 @@ packages: - utf-8-validate dev: false - /@dydxprotocol/v4-localization@0.1.5: - resolution: {integrity: sha512-fiSHWrG8E2HuXkCzWsqyekntlI13hBRACrP0vaYudeSulw+ScGeAqZ5xp9/cANF6PP+H3z1yciNCZOH3OwSDwA==} + /@dydxprotocol/v4-localization@0.1.8: + resolution: {integrity: sha512-ZuM/V2tLVSWyi9pDZLOu8h6HScrzKlFftQ/1iBPNIOU4bBMx83vYgokm0X9hzueWzz0PJ2pzT+EoN81sPlN2bg==} dev: false /@dydxprotocol/v4-proto@0.2.1: diff --git a/src/constants/wallets.ts b/src/constants/wallets.ts index 025040f..a205516 100644 --- a/src/constants/wallets.ts +++ b/src/constants/wallets.ts @@ -339,12 +339,12 @@ export type EvmAddress = `0x${string}`; export type DydxAddress = `dydx${string}`; export const DYDX_CHAIN_INFO: Parameters[0] = { - rpc: '13.59.4.93:26657', - rest: '13.59.4.93:1317', - chainId: 'dydx-testnet-2', + rpc: 'https://dydx-testnet-archive.allthatnode.com:26657', + rest: 'https://dydx-testnet-archive.allthatnode.com:1317', + chainId: 'dydx-testnet-3', chainName: 'dYdX Public Testnet', chainSymbolImageUrl: - 'https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/dydx-testnet-2/chain.png', + 'https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/dydx-testnet-3/chain.png', bech32Config: { bech32PrefixAccPub: 'dydxpub', bech32PrefixValPub: 'dydxvaloperpub', diff --git a/src/hooks/useLocalNotifications.tsx b/src/hooks/useLocalNotifications.tsx index 4c2d600..4bad35c 100644 --- a/src/hooks/useLocalNotifications.tsx +++ b/src/hooks/useLocalNotifications.tsx @@ -6,7 +6,7 @@ import { LocalStorageKey } from '@/constants/localStorage'; import { type TransferNotifcation } from '@/constants/notifications'; import { useAccounts } from '@/hooks/useAccounts'; -import { useSquid } from '@/hooks/useSquid'; +import { SQUID_ERROR_TYPES, useSquid } from '@/hooks/useSquid'; import { useLocalStorage } from './useLocalStorage'; const LocalNotificationsContext = createContext< @@ -66,11 +66,14 @@ const useLocalNotificationsContext = () => { } of transferNotifications) { try { if (currentStatus && currentStatus?.squidTransactionStatus !== 'ongoing') continue; - + const status = await squid?.getStatus({ transactionId: txHash, toChainId, fromChainId }); if (status) statuses[txHash] = status; } catch (error) { - console.error(error); + // ignore not found errors since the route might not be available yet + if (error?.errors?.length && error.errors[0].errorType !== SQUID_ERROR_TYPES.NotFoundError) { + statuses[txHash] = error; + } } } return statuses; diff --git a/src/hooks/useNotificationTypes.tsx b/src/hooks/useNotificationTypes.tsx index 9eacc54..fa6a81d 100644 --- a/src/hooks/useNotificationTypes.tsx +++ b/src/hooks/useNotificationTypes.tsx @@ -1,7 +1,9 @@ import { useCallback, useEffect, useMemo } from 'react'; +import styled, { type AnyStyledComponent } from 'styled-components'; import { useSelector, shallowEqual, useDispatch } from 'react-redux'; import { groupBy } from 'lodash'; +import { AlertType } from '@/constants/alerts'; import { AbacusOrderStatus, ORDER_SIDES, ORDER_STATUS_STRINGS } from '@/constants/abacus'; import { DialogTypes } from '@/constants/dialogs'; import { STRING_KEYS } from '@/constants/localization'; @@ -10,6 +12,7 @@ import { ORDER_SIDE_STRINGS, TRADE_TYPE_STRINGS, TradeTypes } from '@/constants/ import { useLocalNotifications } from '@/hooks/useLocalNotifications'; +import { AlertMessage } from '@/components/AlertMessage'; import { Icon, IconName } from '@/components/Icon'; import { Output, OutputType } from '@/components/Output'; import { TransferStatusToast } from '@/views/TransferStatus'; @@ -119,19 +122,36 @@ export const notificationTypes = [ const { toChainId, status, txHash, toAmount } = transfer; const finished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing'; const type = toChainId === TESTNET_CHAIN_ID ? 'deposit' : 'withdraw'; + // @ts-ignore status.errors is not in the type definition but can be returned + const error = status?.errors?.length ? status?.errors[0] : status?.error; + + // TODO: confirm with design what the description should be + const description = ( +
+ + {type === 'deposit' ? 'Deposit of ' : 'Withdraw of '} + + + + {error && ( + + {stringGetter({ + key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE, + params: { + ERROR_MESSAGE: error.message || stringGetter({ key: STRING_KEYS.UNKNOWN_ERROR }), + }, + })} + + )} +
+ ); trigger( txHash, { icon: , title: stringGetter({ key: getTitleStringKey(type, finished) }), - // TODO: confirm with design what the description should be - description: ( - <> - {type === 'deposit' ? 'Deposit of ' : 'Withdraw of'} - - - ), + description: description, customContent: ( ), - customMenuContent: !finished && , + customMenuContent: !finished && ( +
+ {description} + +
+ ), toastSensitivity: 'foreground', }, [] @@ -149,3 +174,15 @@ export const notificationTypes = [ }, }, ] satisfies NotificationTypeConfig[]; + +const Styled: Record = {}; + +Styled.TransferText = styled.span` + display: inline-flex; + align-items: center; + gap: 0.5ch; +` + +Styled.ErrorMessage = styled.div` + max-width: 13rem; +`; \ No newline at end of file diff --git a/src/hooks/useSquid.tsx b/src/hooks/useSquid.tsx index 8fed15a..5aa5438 100644 --- a/src/hooks/useSquid.tsx +++ b/src/hooks/useSquid.tsx @@ -1,14 +1,17 @@ import { createContext, useContext, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; -import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client-js'; import { Squid } from '@0xsquid/sdk'; -import { CLIENT_NETWORK_CONFIGS, DydxV4Network, isDydxV4Network } from '@/constants/networks'; +import { CLIENT_NETWORK_CONFIGS, isDydxV4Network } from '@/constants/networks'; import { getSelectedNetwork } from '@/state/appSelectors'; export const NATIVE_TOKEN_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; +export enum SQUID_ERROR_TYPES { + NotFoundError = 'NotFoundError', +} + const useSquidContext = () => { const selectedNetwork = useSelector(getSelectedNetwork); const [_, setInitialized] = useState(false); diff --git a/src/views/TransferStatus.tsx b/src/views/TransferStatus.tsx index 37fcc00..6f3bb06 100644 --- a/src/views/TransferStatus.tsx +++ b/src/views/TransferStatus.tsx @@ -7,9 +7,11 @@ import { StatusResponse } from '@0xsquid/sdk'; import { useInterval, useStringGetter } from '@/hooks'; import { STRING_KEYS } from '@/constants/localization'; +import { AlertType } from '@/constants/alerts'; import { formatSeconds } from '@/lib/timeUtils'; +import { AlertMessage } from '@/components/AlertMessage'; import { Output, OutputType } from '@/components/Output'; import { WithReceipt } from '@/components/WithReceipt'; import { Icon, IconName } from '@/components/Icon'; @@ -33,6 +35,9 @@ export const TransferStatusToast = ({ const [open, setOpen] = useState(false); const [secondsLeft, setSecondsLeft] = useState(); + // @ts-ignore status.errors is not in the type definition but can be returned + const error = status?.errors?.length ? status?.errors[0] : status?.error; + const type = useMemo( () => (status?.toChain?.chainData?.chainId === TESTNET_CHAIN_ID ? 'deposit' : 'withdrawal'), [status] @@ -74,6 +79,16 @@ export const TransferStatusToast = ({ }, })} + {error && ( + + {stringGetter({ + key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE, + params: { + ERROR_MESSAGE: error.message || stringGetter({ key: STRING_KEYS.UNKNOWN_ERROR }), + }, + })} + + )} diff --git a/src/views/TransferStatusSteps.tsx b/src/views/TransferStatusSteps.tsx index 1d01aed..cf3d551 100644 --- a/src/views/TransferStatusSteps.tsx +++ b/src/views/TransferStatusSteps.tsx @@ -59,7 +59,7 @@ export const TransferStatusSteps = ({ status }: ElementProps) => { }, ]; - const currentStatus = routeStatus[routeStatus?.length - 1]; + const currentStatus = routeStatus?.[routeStatus?.length - 1]; let currentStep = TransferStatusStep.Bridge; diff --git a/src/views/forms/AccountManagementForms/DepositForm.tsx b/src/views/forms/AccountManagementForms/DepositForm.tsx index bc613f7..17f2163 100644 --- a/src/views/forms/AccountManagementForms/DepositForm.tsx +++ b/src/views/forms/AccountManagementForms/DepositForm.tsx @@ -38,6 +38,7 @@ import { getTransferInputs } from '@/state/inputsSelectors'; import abacusStateManager from '@/lib/abacus'; import { MustBigNumber } from '@/lib/numbers'; import { log } from '@/lib/telemetry'; +import { parseWalletError } from '@/lib/wallet'; import { ChainSelectMenu } from './ChainSelectMenu'; import { TokenSelectMenu } from './TokenSelectMenu'; @@ -286,12 +287,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => { const errorMessage = useMemo(() => { if (error) { - return error?.message - ? stringGetter({ - key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE, - params: { ERROR_MESSAGE: error.message }, - }) - : stringGetter({ key: STRING_KEYS.SOMETHING_WENT_WRONG }); + return parseWalletError({ error, stringGetter }).message; } if (fromAmount) {