Compare commits

..
Author SHA1 Message Date
Bill He 514a2d3ab3 use decimals: 2023-11-06 17:35:57 -08:00
Bill He c58d94e477 Fix decimal overflow bug 2023-11-06 17:26:27 -08:00
26 changed files with 89 additions and 237 deletions
+2 -2
View File
@@ -39,9 +39,9 @@
"@cosmjs/proto-signing": "^0.31.0", "@cosmjs/proto-signing": "^0.31.0",
"@cosmjs/stargate": "^0.31.0", "@cosmjs/stargate": "^0.31.0",
"@cosmjs/tendermint-rpc": "^0.31.0", "@cosmjs/tendermint-rpc": "^0.31.0",
"@dydxprotocol/v4-abacus": "^1.0.19", "@dydxprotocol/v4-abacus": "^1.0.10",
"@dydxprotocol/v4-client-js": "^1.0.0", "@dydxprotocol/v4-client-js": "^1.0.0",
"@dydxprotocol/v4-localization": "^1.0.5", "@dydxprotocol/v4-localization": "^1.0.0",
"@ethersproject/providers": "^5.7.2", "@ethersproject/providers": "^5.7.2",
"@js-joda/core": "^5.5.3", "@js-joda/core": "^5.5.3",
"@radix-ui/react-collapsible": "^1.0.3", "@radix-ui/react-collapsible": "^1.0.3",
+8 -8
View File
@@ -27,14 +27,14 @@ dependencies:
specifier: ^0.31.0 specifier: ^0.31.0
version: 0.31.0 version: 0.31.0
'@dydxprotocol/v4-abacus': '@dydxprotocol/v4-abacus':
specifier: ^1.0.19 specifier: ^1.0.10
version: 1.0.19 version: 1.0.10
'@dydxprotocol/v4-client-js': '@dydxprotocol/v4-client-js':
specifier: ^1.0.0 specifier: ^1.0.0
version: 1.0.0 version: 1.0.0
'@dydxprotocol/v4-localization': '@dydxprotocol/v4-localization':
specifier: ^1.0.5 specifier: ^1.0.0
version: 1.0.5 version: 1.0.0
'@ethersproject/providers': '@ethersproject/providers':
specifier: ^5.7.2 specifier: ^5.7.2
version: 5.7.2 version: 5.7.2
@@ -979,8 +979,8 @@ packages:
resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==} resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==}
dev: true dev: true
/@dydxprotocol/v4-abacus@1.0.19: /@dydxprotocol/v4-abacus@1.0.10:
resolution: {integrity: sha512-XhvSHGr503gNwHWEiOYP7M2uYeLu+Qm4szpE5w5H6hWFCanSGz6zAsTbPuyPLtt7IE1gj5z8mlL9J3tw2AZROg==} resolution: {integrity: sha512-SdxI/JcyxM1ydtR9ImWujeurea3gbxj37qYnw1wz829BQiZ05uNEtll3xJCI0MjpG+01ARsxVys7SHbpjSxwOg==}
dev: false dev: false
/@dydxprotocol/v4-client-js@1.0.0: /@dydxprotocol/v4-client-js@1.0.0:
@@ -1011,8 +1011,8 @@ packages:
- utf-8-validate - utf-8-validate
dev: false dev: false
/@dydxprotocol/v4-localization@1.0.5: /@dydxprotocol/v4-localization@1.0.0:
resolution: {integrity: sha512-zZm3GFp/ORz9YjigQFVBlcWmaxYY+XEdIw8AWN5nEbsuiA2Jay+/OA7lO9+nV1J1p1T+TgLo9iczIHJsAo3IsA==} resolution: {integrity: sha512-U7QjOlzpfSAVEchkNDB3xYRx7okzdeYi83XsnHwI+EHa2quFm/2Y2dhrBkT5fqpZmhcwxb/vG8AZmFSToOf9hA==}
dev: false dev: false
/@dydxprotocol/v4-proto@0.4.1: /@dydxprotocol/v4-proto@0.4.1:
+2 -73
View File
@@ -1,6 +1,6 @@
{ {
"apps": { "apps": {
"ios": { "iOS": {
"scheme": "dydxv4" "scheme": "dydxv4"
} }
}, },
@@ -400,76 +400,6 @@
"signTypedDataDomainName": "dYdX Chain" "signTypedDataDomainName": "dYdX Chain"
} }
}, },
"dydxprotocol-staging-forced-update": {
"name": "v4 Staging Forced Update",
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"isMainNet": false,
"tokens": {
"chain": {
"name": "Dv4TNT",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"decimals": 6,
"image": "/currencies/usdc.png"
}
},
"endpoints": {
"indexers": [
{
"api": "https://indexer.v4staging.dydx.exchange",
"socket": "wss://indexer.v4staging.dydx.exchange"
}
],
"faucet": "https://faucet.v4staging.dydx.exchange",
"validators": [
"https://validator.v4staging.dydx.exchange"
],
"0xsquid": "https://testnet.api.squidrouter.com"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
"privacy": "https://dydx.exchange/privacy",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"documentation": "https://v4-teacher.vercel.app/",
"community": "https://discord.com/invite/dydx",
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
"blogs": "https://www.dydx.foundation/blog"
},
"wallets": {
"walletconnect": {
"client": {
"name": "dYdX v4",
"description": "dYdX v4 App",
"iconUrl": "/logos/dydx-x.png"
},
"v2": {
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue": {
"callbackUrl": "/walletsegue"
},
"images": "/wallets/",
"signTypedDataAction": "dYdX Chain Onboarding",
"signTypedDataDomainName": "dYdX Chain"
},
"apps": {
"ios": {
"minimalVersion": "1.0",
"build":40000,
"url": "https://apps.apple.com/app/dydx/id1564787350"
}
}
},
"dydxprotocol-staging-west": { "dydxprotocol-staging-west": {
"name": "v4 Staging West", "name": "v4 Staging West",
"ethereumChainId": "5", "ethereumChainId": "5",
@@ -963,7 +893,6 @@
"privacy": "https://dydx.exchange/privacy", "privacy": "https://dydx.exchange/privacy",
"statusPage": "https://status.v4testnet.dydx.exchange/", "statusPage": "https://status.v4testnet.dydx.exchange/",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}", "mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"mintscanBase": "https://testnet.mintscan.io/dydx-testnet",
"documentation": "https://docs.dydx.exchange/", "documentation": "https://docs.dydx.exchange/",
"community": "https://discord.com/invite/dydx", "community": "https://discord.com/invite/dydx",
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform", "feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
@@ -1104,7 +1033,7 @@
"tos": "[HTTP link to TOS]", "tos": "[HTTP link to TOS]",
"privacy": "[HTTP link to Privacy Policy]", "privacy": "[HTTP link to Privacy Policy]",
"mintscan": "[HTTP link to Mintscan, with {tx_hash} placeholder]", "mintscan": "[HTTP link to Mintscan, with {tx_hash} placeholder]",
"mintscanBase": "[HTTP link to TOS mintscan base url]", "mintscanBase": "[HTTP link to TOS mintsacn base url]",
"feedback": "[HTTP link to feedback form, can be null]", "feedback": "[HTTP link to feedback form, can be null]",
"blogs": "[HTTP link to blogs, can be null]", "blogs": "[HTTP link to blogs, can be null]",
"foundation": "[HTTP link to foundation, can be null]", "foundation": "[HTTP link to foundation, can be null]",
+6 -12
View File
@@ -1,18 +1,12 @@
import environments from '../../public/configs/env.json'; import environments from '../../public/configs/env.json';
const CURRENT_MODE = ({ // TODO: Update for Mainnet deployment
production: 'MAINNET', export const AVAILABLE_ENVIRONMENTS =
testnet: 'TESTNET', import.meta.env.MODE === 'production'
staging: 'DEV', ? environments.deployments.TESTNET
development: 'DEV', : environments.deployments.DEV;
}[import.meta.env.MODE] ?? 'MAINNET') as 'MAINNET' | 'TESTNET' | 'DEV';
export const isMainnet = CURRENT_MODE === 'MAINNET'; export const CURRENT_ABACUS_DEPLOYMENT = import.meta.env.MODE === 'production' ? 'TESTNET' : 'DEV';
export const isTestnet = CURRENT_MODE === 'TESTNET';
export const isDev = CURRENT_MODE === 'DEV';
export const AVAILABLE_ENVIRONMENTS = environments.deployments[CURRENT_MODE];
export const CURRENT_ABACUS_DEPLOYMENT = CURRENT_MODE;
export const ENVIRONMENT_CONFIG_MAP = environments.environments; export const ENVIRONMENT_CONFIG_MAP = environments.environments;
export type DydxNetwork = keyof typeof ENVIRONMENT_CONFIG_MAP; export type DydxNetwork = keyof typeof ENVIRONMENT_CONFIG_MAP;
export const DEFAULT_APP_ENVIRONMENT = AVAILABLE_ENVIRONMENTS.default as DydxNetwork; export const DEFAULT_APP_ENVIRONMENT = AVAILABLE_ENVIRONMENTS.default as DydxNetwork;
-2
View File
@@ -42,7 +42,6 @@ const useAccountsContext = () => {
selectedWalletError, selectedWalletError,
evmAddress, evmAddress,
signerWagmi, signerWagmi,
publicClientWagmi,
dydxAddress: connectedDydxAddress, dydxAddress: connectedDydxAddress,
signerGraz, signerGraz,
} = useWalletConnection(); } = useWalletConnection();
@@ -297,7 +296,6 @@ const useAccountsContext = () => {
// Wallet connection (EVM) // Wallet connection (EVM)
evmAddress, evmAddress,
signerWagmi, signerWagmi,
publicClientWagmi,
// Wallet connection (Cosmos) // Wallet connection (Cosmos)
signerGraz, signerGraz,
+1 -2
View File
@@ -1,4 +1,3 @@
import { isDev } from '@/constants/networks';
import { WalletType } from '@/constants/wallets'; import { WalletType } from '@/constants/wallets';
import { isTruthy } from '@/lib/isTruthy'; import { isTruthy } from '@/lib/isTruthy';
@@ -7,7 +6,7 @@ export const useDisplayedWallets = () => {
return [ return [
WalletType.MetaMask, WalletType.MetaMask,
isDev && WalletType.Keplr, import.meta.env.MODE !== 'production' && WalletType.Keplr,
WalletType.WalletConnect2, WalletType.WalletConnect2,
+2 -4
View File
@@ -7,11 +7,9 @@ import { AlertType } from '@/constants/alerts';
import { AbacusOrderStatus, ORDER_SIDES } from '@/constants/abacus'; import { AbacusOrderStatus, ORDER_SIDES } from '@/constants/abacus';
import { DialogTypes } from '@/constants/dialogs'; import { DialogTypes } from '@/constants/dialogs';
import { STRING_KEYS, StringKey } from '@/constants/localization'; import { STRING_KEYS, StringKey } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { type NotificationTypeConfig, NotificationType } from '@/constants/notifications'; import { type NotificationTypeConfig, NotificationType } from '@/constants/notifications';
import { ORDER_SIDE_STRINGS } from '@/constants/trade'; import { ORDER_SIDE_STRINGS } from '@/constants/trade';
import { useSelectedNetwork } from '@/hooks';
import { useLocalNotifications } from '@/hooks/useLocalNotifications'; import { useLocalNotifications } from '@/hooks/useLocalNotifications';
import { Icon, IconName } from '@/components/Icon'; import { Icon, IconName } from '@/components/Icon';
@@ -25,6 +23,7 @@ import { OrderStatusIcon } from '@/views/OrderStatusIcon';
import { useStringGetter } from './useStringGetter'; import { useStringGetter } from './useStringGetter';
import { TransferStatusSteps } from '@/views/TransferStatusSteps'; import { TransferStatusSteps } from '@/views/TransferStatusSteps';
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client-js';
export const notificationTypes = [ export const notificationTypes = [
{ {
@@ -125,7 +124,6 @@ export const notificationTypes = [
useTrigger: ({ trigger }) => { useTrigger: ({ trigger }) => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { transferNotifications } = useLocalNotifications(); const { transferNotifications } = useLocalNotifications();
const { selectedNetwork } = useSelectedNetwork();
const getTitleStringKey = useCallback((type: 'deposit' | 'withdrawal', finished: boolean) => { const getTitleStringKey = useCallback((type: 'deposit' | 'withdrawal', finished: boolean) => {
if (type === 'deposit' && !finished) return STRING_KEYS.DEPOSIT_IN_PROGRESS; if (type === 'deposit' && !finished) return STRING_KEYS.DEPOSIT_IN_PROGRESS;
@@ -138,7 +136,7 @@ export const notificationTypes = [
for (const transfer of transferNotifications) { for (const transfer of transferNotifications) {
const { fromChainId, status, txHash, toAmount } = transfer; const { fromChainId, status, txHash, toAmount } = transfer;
const finished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing'; const finished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing';
const type = fromChainId === ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId ? 'withdrawal' : 'deposit'; const type = fromChainId === TESTNET_CHAIN_ID ? 'withdrawal' : 'deposit';
// @ts-ignore status.errors is not in the type definition but can be returned // @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 error = status?.errors?.length ? status?.errors[0] : status?.error;
+6
View File
@@ -350,6 +350,11 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
placeOrderParams = abacusStateManager.placeOrder(callback); placeOrderParams = abacusStateManager.placeOrder(callback);
} }
track(AnalyticsEvent.TradePlaceOrder, {
...placeOrderParams,
isClosePosition,
} as HumanReadablePlaceOrderPayload & { isClosePosition: boolean });
return placeOrderParams; return placeOrderParams;
}, },
[subaccountClient] [subaccountClient]
@@ -378,6 +383,7 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
}) => { }) => {
const callback = (success: boolean, parsingError?: Nullable<ParsingError>) => { const callback = (success: boolean, parsingError?: Nullable<ParsingError>) => {
if (success) { if (success) {
track(AnalyticsEvent.TradeCancelOrder);
onSuccess?.(); onSuccess?.();
} else { } else {
onError?.({ errorStringKey: parsingError?.stringKey }); onError?.({ errorStringKey: parsingError?.stringKey });
+9 -11
View File
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useState, useMemo } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { LocalStorageKey } from '@/constants/localStorage'; import { LocalStorageKey } from '@/constants/localStorage';
@@ -90,21 +90,19 @@ export const useWalletConnection = () => {
const selectedNetwork = useSelector(getSelectedNetwork); const selectedNetwork = useSelector(getSelectedNetwork);
const walletConnectConfig = ENVIRONMENT_CONFIG_MAP[selectedNetwork].wallets.walletconnect; const walletConnectConfig = ENVIRONMENT_CONFIG_MAP[selectedNetwork].wallets.walletconnect;
const wagmiConnector = useMemo(
() => const { connectAsync: connectWagmi } =
walletType && walletConnectionType walletType && walletConnectionType
? resolveWagmiConnector({ ? useConnectWagmi({
connector: resolveWagmiConnector({
walletType, walletType,
walletConnection: { walletConnection: {
type: walletConnectionType, type: walletConnectionType,
}, },
walletConnectConfig, walletConnectConfig,
}) }),
: undefined, })
[walletConnectConfig, walletType, walletConnectionType] : useConnectWagmi();
);
const { connectAsync: connectWagmi } = useConnectWagmi({ connector: wagmiConnector })
const { suggestAndConnect: connectGraz } = useConnectGraz(); const { suggestAndConnect: connectGraz } = useConnectGraz();
const connectWallet = useCallback( const connectWallet = useCallback(
+2 -2
View File
@@ -3,7 +3,7 @@ import styled, { type AnyStyledComponent, css } from 'styled-components';
import { AbacusApiStatus } from '@/constants/abacus'; import { AbacusApiStatus } from '@/constants/abacus';
import { ButtonSize } from '@/constants/buttons'; import { ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP, isDev } from '@/constants/networks'; import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { useApiState, useSelectedNetwork, useStringGetter } from '@/hooks'; import { useApiState, useSelectedNetwork, useStringGetter } from '@/hooks';
import { ChatIcon, LinkOutIcon } from '@/icons'; import { ChatIcon, LinkOutIcon } from '@/icons';
@@ -75,7 +75,7 @@ export const FooterDesktop = () => {
)} )}
</Styled.Row> </Styled.Row>
{isDev && ( {import.meta.env.MODE !== 'production' && (
<Styled.Details <Styled.Details
withSeparators withSeparators
items={[ items={[
-1
View File
@@ -236,5 +236,4 @@ Styled.IconButton = styled(IconButton)<{ size?: string }>`
${headerMixins.button} ${headerMixins.button}
--button-border: none; --button-border: none;
--button-icon-size: 1rem; --button-icon-size: 1rem;
--button-padding: 0 0.5em;
`; `;
+10 -11
View File
@@ -30,7 +30,7 @@ import {
import { DialogTypes } from '@/constants/dialogs'; import { DialogTypes } from '@/constants/dialogs';
import { UNCOMMITTED_ORDER_TIMEOUT_MS } from '@/constants/trade'; import { UNCOMMITTED_ORDER_TIMEOUT_MS } from '@/constants/trade';
import { ENVIRONMENT_CONFIG_MAP, DydxNetwork, isTestnet } from '@/constants/networks'; import { QUANTUM_MULTIPLIER } from '@/constants/numbers';
import { RootStore } from '@/state/_store'; import { RootStore } from '@/state/_store';
import { addUncommittedOrderClientId, removeUncommittedOrderClientId } from '@/state/account'; import { addUncommittedOrderClientId, removeUncommittedOrderClientId } from '@/state/account';
@@ -198,13 +198,8 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
const parsedTx = this.parseToPrimitives(tx); const parsedTx = this.parseToPrimitives(tx);
const hash = parsedTx?.hash; const hash = parsedTx?.hash;
if (isTestnet) { if (import.meta.env.MODE === 'production') {
console.log( console.log(`https://testnet.mintscan.io/dydx-testnet/txs/${hash}`);
`${
ENVIRONMENT_CONFIG_MAP[this.compositeClient.network.getString() as DydxNetwork]?.links
?.mintscanBase
}/txs/${hash}`
);
} else console.log(`txHash: ${hash}`); } else console.log(`txHash: ${hash}`);
return JSON.stringify(parsedTx); return JSON.stringify(parsedTx);
@@ -234,7 +229,7 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
orderFlags, orderFlags,
clobPairId, clobPairId,
goodTilBlock || undefined, goodTilBlock || undefined,
goodTilBlockTime || undefined goodTilBlockTime || undefined,
); );
const parsedTx = this.parseToPrimitives(tx); const parsedTx = this.parseToPrimitives(tx);
@@ -300,11 +295,15 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
if (!this.localWallet) { if (!this.localWallet) {
throw new Error('Missing compositeClient or localWallet'); throw new Error('Missing compositeClient or localWallet');
} }
const msg = compositeClient?.sendTokenMessage(this.localWallet, amount, recipient); const msg = compositeClient?.sendTokenMessage(
this.localWallet,
amount,
recipient,
);
resolve([msg]); resolve([msg]);
}), }),
this.compositeClient?.validatorClient?.post.defaultDydxGasPrice this.compositeClient?.validatorClient?.post.defaultDydxGasPrice,
); );
const parsedTx = this.parseToPrimitives(tx); const parsedTx = this.parseToPrimitives(tx);
+5
View File
@@ -1,5 +1,10 @@
import type { AbacusFileSystemProtocol, FileLocation, Nullable } from '@/constants/abacus'; import type { AbacusFileSystemProtocol, FileLocation, Nullable } from '@/constants/abacus';
export const ENDPOINTS_PATH =
import.meta.env.MODE === 'production'
? 'config/prod/endpoints.json'
: 'config/staging/dev_endpoints.json';
class AbacusFileSystem implements AbacusFileSystemProtocol { class AbacusFileSystem implements AbacusFileSystemProtocol {
readTextFile(location: FileLocation, path: string): Nullable<string> { readTextFile(location: FileLocation, path: string): Nullable<string> {
return null; return null;
+1 -1
View File
@@ -41,7 +41,7 @@ import AbacusStateNotifier from './stateNotification';
import AbacusLocalizer from './localizer'; import AbacusLocalizer from './localizer';
import AbacusFormatter from './formatter'; import AbacusFormatter from './formatter';
import AbacusThreading from './threading'; import AbacusThreading from './threading';
import AbacusFileSystem from './filesystem'; import AbacusFileSystem, { ENDPOINTS_PATH } from './filesystem';
import { LocaleSeparators } from '../numbers'; import { LocaleSeparators } from '../numbers';
class AbacusStateManager { class AbacusStateManager {
private store: RootStore | undefined; private store: RootStore | undefined;
+2 -3
View File
@@ -1,6 +1,5 @@
import type { AbacusWebsocketProtocol } from '@/constants/abacus'; import type { AbacusWebsocketProtocol } from '@/constants/abacus';
import type { TradingViewBar } from '@/constants/candles'; import type { TradingViewBar } from '@/constants/candles';
import { isDev } from '@/constants/networks';
import { import {
PING_INTERVAL_MS, PING_INTERVAL_MS,
@@ -162,13 +161,13 @@ class AbacusWebsocket implements Omit<AbacusWebsocketProtocol, '__doNotUseOrImpl
this.socket.onclose = (e) => { this.socket.onclose = (e) => {
this.connectedCallback?.(false); this.connectedCallback?.(false);
if (!isDev) return; if (import.meta.env.MODE === 'production') return;
console.warn('AbacusStateManager > WS > close > ', e); console.warn('AbacusStateManager > WS > close > ', e);
}; };
this.socket.onerror = (e) => { this.socket.onerror = (e) => {
this.connectedCallback?.(false); this.connectedCallback?.(false);
if (!isDev) return; if (import.meta.env.MODE === 'production') return;
console.error('AbacusStateManager > WS > error > ', e); console.error('AbacusStateManager > WS > error > ', e);
}; };
}; };
+1 -3
View File
@@ -1,7 +1,5 @@
import { isDev } from "@/constants/networks";
export const log = (location: string, error: Error, metadata?: any) => { export const log = (location: string, error: Error, metadata?: any) => {
if (isDev) { if (import.meta.env.MODE !== 'production') {
console.warn('telemetry/log:', { location, error, metadata }); console.warn('telemetry/log:', { location, error, metadata });
} }
+1 -2
View File
@@ -2,7 +2,6 @@ import type { PayloadAction } from '@reduxjs/toolkit';
import { AbacusApiStatus } from '@/constants/abacus'; import { AbacusApiStatus } from '@/constants/abacus';
import { DialogTypes } from '@/constants/dialogs'; import { DialogTypes } from '@/constants/dialogs';
import { isDev } from '@/constants/networks';
import { setApiState, setSelectedNetwork } from '@/state/app'; import { setApiState, setSelectedNetwork } from '@/state/app';
import { resetPerpetualsState } from '@/state/perpetuals'; import { resetPerpetualsState } from '@/state/perpetuals';
@@ -31,7 +30,7 @@ export default (store: any) => (next: any) => async (action: PayloadAction<any>)
store.dispatch( store.dispatch(
openDialog({ openDialog({
type: DialogTypes.ExchangeOffline, type: DialogTypes.ExchangeOffline,
dialogProps: { preventClose: !isDev }, dialogProps: { preventClose: import.meta.env.MODE === 'production' },
}) })
); );
} }
-9
View File
@@ -48,15 +48,6 @@ export const getTradeInputErrors = (state: RootState) => {
return currentInput === 'trade' ? getInputErrors(state) : []; return currentInput === 'trade' ? getInputErrors(state) : [];
}; };
/**
* @param state
* @returns input errors for Close position
*/
export const getClosePositionInputErrors = (state: RootState) => {
const currentInput = state.inputs.current;
return currentInput === 'closePosition' ? getInputErrors(state) : [];
};
/** /**
* @param state * @param state
* @returns input errors for Transfer * @returns input errors for Transfer
+3 -12
View File
@@ -34,7 +34,7 @@ export const TransferStatusToast = ({
}: ElementProps) => { }: ElementProps) => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const [open, setOpen] = useState<boolean>(false); const [open, setOpen] = useState<boolean>(false);
const [secondsLeft, setSecondsLeft] = useState<number>(0); const [secondsLeft, setSecondsLeft] = useState<number | undefined>();
// @ts-ignore status.errors is not in the type definition but can be returned // @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 error = status?.errors?.length ? status?.errors[0] : status?.error;
@@ -49,23 +49,14 @@ export const TransferStatusToast = ({
if (!status) return <LoadingDots size={3} />; if (!status) return <LoadingDots size={3} />;
const inProgressStatusString =
type === 'deposit'
? secondsLeft > 0
? STRING_KEYS.DEPOSIT_STATUS
: STRING_KEYS.DEPOSIT_STATUS_SHORTLY
: secondsLeft > 0
? STRING_KEYS.WITHDRAW_STATUS
: STRING_KEYS.WITHDRAW_STATUS_SHORTLY;
const statusString = const statusString =
type === 'deposit' type === 'deposit'
? status?.squidTransactionStatus === 'success' ? status?.squidTransactionStatus === 'success'
? STRING_KEYS.DEPOSIT_COMPLETE ? STRING_KEYS.DEPOSIT_COMPLETE
: inProgressStatusString : STRING_KEYS.DEPOSIT_STATUS
: status?.squidTransactionStatus === 'success' : status?.squidTransactionStatus === 'success'
? STRING_KEYS.WITHDRAW_COMPLETE ? STRING_KEYS.WITHDRAW_COMPLETE
: inProgressStatusString; : STRING_KEYS.WITHDRAW_STATUS;
return ( return (
<Styled.Root open={open} onOpenChange={setOpen}> <Styled.Root open={open} onOpenChange={setOpen}>
+4 -13
View File
@@ -2,7 +2,7 @@ import { useMemo } from 'react';
import styled, { css, keyframes, type AnyStyledComponent } from 'styled-components'; import styled, { css, keyframes, type AnyStyledComponent } from 'styled-components';
import { StatusResponse } from '@0xsquid/sdk'; import { StatusResponse } from '@0xsquid/sdk';
import { useStringGetter, useSelectedNetwork, useURLConfigs } from '@/hooks'; import { useStringGetter } from '@/hooks';
import { Link } from '@/components/Link'; import { Link } from '@/components/Link';
import { Icon, IconName } from '@/components/Icon'; import { Icon, IconName } from '@/components/Icon';
@@ -11,7 +11,6 @@ import { LoadingSpinner } from '@/components/Loading/LoadingSpinner';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
type ElementProps = { type ElementProps = {
status?: StatusResponse; status?: StatusResponse;
@@ -27,17 +26,12 @@ enum TransferStatusStep {
export const TransferStatusSteps = ({ status, type }: ElementProps) => { export const TransferStatusSteps = ({ status, type }: ElementProps) => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { selectedNetwork } = useSelectedNetwork();
const { mintscan: mintscanTxUrl } = useURLConfigs();
const dydxChainId = ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId;
const { currentStep, steps } = useMemo(() => { const { currentStep, steps } = useMemo(() => {
const routeStatus = status?.routeStatus; const routeStatus = status?.routeStatus;
const fromChain = status?.fromChain?.chainData?.chainId; const fromChain = status?.fromChain?.chainData?.chainId;
const toChain = status?.toChain?.chainData?.chainId; const toChain = status?.toChain?.chainData?.chainId;
const currentStatus = routeStatus?.[routeStatus?.length - 1];
const steps = [ const steps = [
{ {
label: stringGetter({ label: stringGetter({
@@ -60,15 +54,12 @@ export const TransferStatusSteps = ({ status, type }: ElementProps) => {
}, },
}), }),
step: TransferStatusStep.ToChain, step: TransferStatusStep.ToChain,
link: link: status?.toChain?.transactionUrl,
type === 'withdrawal'
? status?.toChain?.transactionUrl
: currentStatus?.chainId === dydxChainId && currentStatus?.txHash
? `${mintscanTxUrl?.replace('{tx_hash}', currentStatus.txHash)}`
: undefined,
}, },
]; ];
const currentStatus = routeStatus?.[routeStatus?.length - 1];
let currentStep = TransferStatusStep.Bridge; let currentStep = TransferStatusStep.Bridge;
if (!routeStatus?.length) { if (!routeStatus?.length) {
+5 -14
View File
@@ -1,7 +1,6 @@
import { useState } from 'react'; import { useState } from 'react';
import styled, { type AnyStyledComponent } from 'styled-components'; import styled, { type AnyStyledComponent } from 'styled-components';
import { isMainnet } from '@/constants/networks';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { useBreakpoints, useStringGetter } from '@/hooks'; import { useBreakpoints, useStringGetter } from '@/hooks';
@@ -28,22 +27,14 @@ export const DepositDialog = ({ setIsOpen }: ElementProps) => {
isOpen isOpen
setIsOpen={setIsOpen} setIsOpen={setIsOpen}
title={stringGetter({ key: STRING_KEYS.DEPOSIT })} title={stringGetter({ key: STRING_KEYS.DEPOSIT })}
description={ description={showTestDeposit && 'Test funds will be sent directly to your dYdX account.'}
!isMainnet && showTestDeposit && 'Test funds will be sent directly to your dYdX account.'
}
placement={isMobile ? DialogPlacement.FullScreen : DialogPlacement.Default} placement={isMobile ? DialogPlacement.FullScreen : DialogPlacement.Default}
> >
<Styled.Content> <Styled.Content>
{!isMainnet && showTestDeposit ? ( {showTestDeposit ? <TestnetDepositForm onDeposit={closeDialog} /> : <DepositForm />}
<TestnetDepositForm onDeposit={closeDialog} /> <Styled.TextToggle onClick={() => setShowTestDeposit(!showTestDeposit)}>
) : ( {showTestDeposit ? 'Show deposit form (Under Construction)' : 'Show test faucet'}
<DepositForm /> </Styled.TextToggle>
)}
{!isMainnet && (
<Styled.TextToggle onClick={() => setShowTestDeposit(!showTestDeposit)}>
{showTestDeposit ? 'Show deposit form (Under Construction)' : 'Show test faucet'}
</Styled.TextToggle>
)}
</Styled.Content> </Styled.Content>
</Dialog> </Dialog>
); );
+1 -2
View File
@@ -5,7 +5,6 @@ import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { AbacusApiStatus } from '@/constants/abacus'; import { AbacusApiStatus } from '@/constants/abacus';
import { DialogTypes } from '@/constants/dialogs'; import { DialogTypes } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { isDev } from '@/constants/networks';
import { useApiState, useStringGetter } from '@/hooks'; import { useApiState, useStringGetter } from '@/hooks';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
@@ -46,7 +45,7 @@ export const ExchangeOfflineDialog = ({ preventClose, setIsOpen }: ElementProps)
> >
<Styled.Content> <Styled.Content>
<p>{statusErrorMessage}</p> <p>{statusErrorMessage}</p>
{isDev && <NetworkSelectMenu />} {import.meta.env.MODE !== 'production' && <NetworkSelectMenu />}
</Styled.Content> </Styled.Content>
</Dialog> </Dialog>
); );
@@ -2,6 +2,7 @@ import { type FormEvent, useCallback, useEffect, useMemo, useState } from 'react
import styled, { type AnyStyledComponent } from 'styled-components'; import styled, { type AnyStyledComponent } from 'styled-components';
import { type NumberFormatValues } from 'react-number-format'; import { type NumberFormatValues } from 'react-number-format';
import { shallowEqual, useSelector } from 'react-redux'; import { shallowEqual, useSelector } from 'react-redux';
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client-js';
import { parseUnits } from 'viem' import { parseUnits } from 'viem'
import erc20 from '@/abi/erc20.json'; import erc20 from '@/abi/erc20.json';
@@ -9,14 +10,14 @@ import { TransferInputField, TransferInputTokenResource, TransferType } from '@/
import { AlertType } from '@/constants/alerts'; import { AlertType } from '@/constants/alerts';
import { ButtonSize } from '@/constants/buttons'; import { ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { NumberSign } from '@/constants/numbers'; import { NumberSign } from '@/constants/numbers';
import type { EvmAddress } from '@/constants/wallets'; import type { EvmAddress } from '@/constants/wallets';
import { useAccounts, useDebounce, useStringGetter, useSelectedNetwork } from '@/hooks'; import { useAccounts, useDebounce, useStringGetter } from '@/hooks';
import { useAccountBalance, CHAIN_DEFAULT_TOKEN_ADDRESS } from '@/hooks/useAccountBalance'; import { useAccountBalance, CHAIN_DEFAULT_TOKEN_ADDRESS } from '@/hooks/useAccountBalance';
import { useLocalNotifications } from '@/hooks/useLocalNotifications'; import { useLocalNotifications } from '@/hooks/useLocalNotifications';
import { NATIVE_TOKEN_ADDRESS, useSquid } from '@/hooks/useSquid'; import { NATIVE_TOKEN_ADDRESS, useSquid } from '@/hooks/useSquid';
import { useWalletConnection } from '@/hooks/useWalletConnection';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
import { formMixins } from '@/styles/formMixins'; import { formMixins } from '@/styles/formMixins';
@@ -53,9 +54,9 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const [error, setError] = useState<Error | null>(null); const [error, setError] = useState<Error | null>(null);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { selectedNetwork } = useSelectedNetwork();
const { evmAddress, signerWagmi, publicClientWagmi } = useAccounts(); const { evmAddress, signerWagmi } = useAccounts();
const { publicClientWagmi } = useWalletConnection();
const { addTransferNotification } = useLocalNotifications(); const { addTransferNotification } = useLocalNotifications();
@@ -238,7 +239,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
if (txHash) { if (txHash) {
addTransferNotification({ addTransferNotification({
txHash: txHash, txHash: txHash,
toChainId: ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId, toChainId: TESTNET_CHAIN_ID,
fromChainId: chainIdStr || undefined, fromChainId: chainIdStr || undefined,
toAmount: summary?.usdcSize || undefined, toAmount: summary?.usdcSize || undefined,
triggeredAt: Date.now(), triggeredAt: Date.now(),
@@ -3,13 +3,13 @@ import type { ChangeEvent, FormEvent } from 'react';
import styled, { type AnyStyledComponent } from 'styled-components'; import styled, { type AnyStyledComponent } from 'styled-components';
import type { NumberFormatValues } from 'react-number-format'; import type { NumberFormatValues } from 'react-number-format';
import { shallowEqual, useSelector } from 'react-redux'; import { shallowEqual, useSelector } from 'react-redux';
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client-js';
import { isAddress } from 'viem'; import { isAddress } from 'viem';
import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus'; import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus';
import { AlertType } from '@/constants/alerts'; import { AlertType } from '@/constants/alerts';
import { ButtonSize } from '@/constants/buttons'; import { ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { NumberSign } from '@/constants/numbers'; import { NumberSign } from '@/constants/numbers';
import { import {
@@ -17,7 +17,6 @@ import {
useDebounce, useDebounce,
useDydxClient, useDydxClient,
useRestrictions, useRestrictions,
useSelectedNetwork,
useStringGetter, useStringGetter,
useSubaccount, useSubaccount,
} from '@/hooks'; } from '@/hooks';
@@ -53,7 +52,6 @@ export const WithdrawForm = () => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const [error, setError] = useState<string>(); const [error, setError] = useState<string>();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { selectedNetwork } = useSelectedNetwork();
const { sendSquidWithdraw } = useSubaccount(); const { sendSquidWithdraw } = useSubaccount();
const { freeCollateral } = useSelector(getSubaccount, shallowEqual) || {}; const { freeCollateral } = useSelector(getSubaccount, shallowEqual) || {};
@@ -166,7 +164,7 @@ export const WithdrawForm = () => {
const hash = `0x${Buffer.from(txHash.hash).toString('hex')}`; const hash = `0x${Buffer.from(txHash.hash).toString('hex')}`;
addTransferNotification({ addTransferNotification({
txHash: hash, txHash: hash,
fromChainId: ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId, fromChainId: TESTNET_CHAIN_ID,
toChainId: chainIdStr || undefined, toChainId: chainIdStr || undefined,
toAmount: debouncedAmountBN.toNumber(), toAmount: debouncedAmountBN.toNumber(),
triggeredAt: Date.now(), triggeredAt: Date.now(),
+7 -31
View File
@@ -4,10 +4,8 @@ import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { import {
ClosePositionInputField, ClosePositionInputField,
ValidationError,
type HumanReadablePlaceOrderPayload, type HumanReadablePlaceOrderPayload,
type Nullable, type Nullable,
ErrorType,
} from '@/constants/abacus'; } from '@/constants/abacus';
import { AlertType } from '@/constants/alerts'; import { AlertType } from '@/constants/alerts';
import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons'; import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons';
@@ -37,14 +35,13 @@ import { PositionPreview } from '@/views/forms/TradeForm/PositionPreview';
import { getCurrentMarketPositionData } from '@/state/accountSelectors'; import { getCurrentMarketPositionData } from '@/state/accountSelectors';
import { getCurrentMarketAssetData } from '@/state/assetsSelectors'; import { getCurrentMarketAssetData } from '@/state/assetsSelectors';
import { getClosePositionInputErrors, getInputClosePositionData } from '@/state/inputsSelectors'; import { getInputClosePositionData } from '@/state/inputsSelectors';
import { getCurrentMarketConfig, getCurrentMarketId } from '@/state/perpetualsSelectors'; import { getCurrentMarketConfig, getCurrentMarketId } from '@/state/perpetualsSelectors';
import { closeDialog } from '@/state/dialogs'; import { closeDialog } from '@/state/dialogs';
import { getCurrentInput } from '@/state/inputsSelectors'; import { getCurrentInput } from '@/state/inputsSelectors';
import abacusStateManager from '@/lib/abacus'; import abacusStateManager from '@/lib/abacus';
import { MustBigNumber } from '@/lib/numbers'; import { MustBigNumber } from '@/lib/numbers';
import { getTradeInputAlert } from '@/lib/tradeData';
const MAX_KEY = 'MAX'; const MAX_KEY = 'MAX';
@@ -84,38 +81,15 @@ export const ClosePositionForm = ({
const market = useSelector(getCurrentMarketId); const market = useSelector(getCurrentMarketId);
const { id } = useSelector(getCurrentMarketAssetData, shallowEqual) || {}; const { id } = useSelector(getCurrentMarketAssetData, shallowEqual) || {};
const { stepSizeDecimals, tickSizeDecimals } = const { stepSizeDecimals } = useSelector(getCurrentMarketConfig, shallowEqual) || {};
useSelector(getCurrentMarketConfig, shallowEqual) || {};
const { size: sizeData, summary } = useSelector(getInputClosePositionData, shallowEqual) || {}; const { size: sizeData, summary } = useSelector(getInputClosePositionData, shallowEqual) || {};
const { size, percent } = sizeData || {}; const { size, percent } = sizeData || {};
const currentInput = useSelector(getCurrentInput); const currentInput = useSelector(getCurrentInput);
const closePositionInputErrors = useSelector(getClosePositionInputErrors, shallowEqual);
const currentPositionData = useSelector(getCurrentMarketPositionData, shallowEqual); const currentPositionData = useSelector(getCurrentMarketPositionData, shallowEqual);
const { size: currentPositionSize } = currentPositionData || {}; const { size: currentPositionSize } = currentPositionData || {};
const { current: currentSize } = currentPositionSize || {}; const { current: currentSize } = currentPositionSize || {};
const currentSizeBN = MustBigNumber(currentSize).abs(); const currentSizeBN = MustBigNumber(currentSize).abs();
const hasInputErrors = closePositionInputErrors?.some(
(error: ValidationError) => error.type !== ErrorType.warning
);
const inputAlert = getTradeInputAlert({
abacusInputErrors: closePositionInputErrors ?? [],
stringGetter,
stepSizeDecimals,
tickSizeDecimals,
});
let alertContent;
let alertType = AlertType.Error;
if (closePositionError) {
alertContent = closePositionError;
} else if (inputAlert) {
alertContent = inputAlert?.alertString;
alertType = inputAlert?.type;
}
useEffect(() => { useEffect(() => {
if (currentStep && currentStep !== MobilePlaceOrderSteps.EditOrder) return; if (currentStep && currentStep !== MobilePlaceOrderSteps.EditOrder) return;
@@ -208,7 +182,9 @@ export const ClosePositionForm = ({
}); });
}; };
const alertMessage = alertContent && <AlertMessage type={alertType}>{alertContent}</AlertMessage>; const alertMessage = closePositionError && (
<AlertMessage type={AlertType.Error}>{closePositionError}</AlertMessage>
);
const inputs = ( const inputs = (
<Styled.InputsColumn> <Styled.InputsColumn>
@@ -289,8 +265,8 @@ export const ClosePositionForm = ({
<PlaceOrderButtonAndReceipt <PlaceOrderButtonAndReceipt
isLoading={isClosingPosition} isLoading={isClosingPosition}
hasValidationErrors={hasInputErrors} hasValidationErrors={!size}
actionStringKey={inputAlert?.actionStringKey} actionStringKey={!size ? STRING_KEYS.ENTER_AMOUNT : undefined}
summary={summary ?? undefined} summary={summary ?? undefined}
currentStep={currentStep} currentStep={currentStep}
isClosePosition isClosePosition
+3 -10
View File
@@ -7,7 +7,7 @@ import { ButtonAction } from '@/constants/buttons';
import { DialogTypes } from '@/constants/dialogs'; import { DialogTypes } from '@/constants/dialogs';
import { STRING_KEYS, StringGetterFunction } from '@/constants/localization'; import { STRING_KEYS, StringGetterFunction } from '@/constants/localization';
import { useBreakpoints, useStringGetter, useURLConfigs } from '@/hooks'; import { useBreakpoints, useStringGetter } from '@/hooks';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
import { tradeViewMixins } from '@/styles/tradeViewMixins'; import { tradeViewMixins } from '@/styles/tradeViewMixins';
@@ -40,13 +40,11 @@ const getTransferHistoryTableColumnDef = ({
key, key,
stringGetter, stringGetter,
width, width,
mintscanTxUrl,
}: { }: {
key: TransferHistoryTableColumnKey; key: TransferHistoryTableColumnKey;
isTablet?: boolean; isTablet?: boolean;
stringGetter: StringGetterFunction; stringGetter: StringGetterFunction;
width?: ColumnSize; width?: ColumnSize;
mintscanTxUrl?: string;
}): ColumnDef<SubaccountTransfer> => ({ }): ColumnDef<SubaccountTransfer> => ({
width, width,
...( ...(
@@ -100,12 +98,9 @@ const getTransferHistoryTableColumnDef = ({
columnKey: TransferHistoryTableColumnKey.TxHash, columnKey: TransferHistoryTableColumnKey.TxHash,
getCellValue: (row) => row.transactionHash, getCellValue: (row) => row.transactionHash,
label: stringGetter({ key: STRING_KEYS.TRANSACTION }), label: stringGetter({ key: STRING_KEYS.TRANSACTION }),
renderCell: ({ transactionHash }) => renderCell: ({ transactionHash, resources }) =>
transactionHash ? ( transactionHash ? (
<Styled.TxHash <Styled.TxHash withIcon href={resources.blockExplorerUrl}>
withIcon
href={`${mintscanTxUrl?.replace('{tx_hash}', transactionHash)}`}
>
{truncateAddress(transactionHash, '')} {truncateAddress(transactionHash, '')}
</Styled.TxHash> </Styled.TxHash>
) : ( ) : (
@@ -135,7 +130,6 @@ export const TransferHistoryTable = ({
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const dispatch = useDispatch(); const dispatch = useDispatch();
const { isMobile, isTablet } = useBreakpoints(); const { isMobile, isTablet } = useBreakpoints();
const { mintscan: mintscanTxUrl } = useURLConfigs();
const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual); const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual);
@@ -152,7 +146,6 @@ export const TransferHistoryTable = ({
isTablet, isTablet,
stringGetter, stringGetter,
width: columnWidths?.[key], width: columnWidths?.[key],
mintscanTxUrl,
}) })
)} )}
slotEmpty={ slotEmpty={