Merge branch 'main' into abacus-tracking
@@ -37,7 +37,7 @@
|
||||
"@cosmjs/proto-signing": "^0.31.0",
|
||||
"@cosmjs/stargate": "^0.31.0",
|
||||
"@cosmjs/tendermint-rpc": "^0.31.0",
|
||||
"@dydxprotocol/abacus": "^0.4.1",
|
||||
"@dydxprotocol/abacus": "^0.4.6",
|
||||
"@dydxprotocol/v4-client-js": "^0.32.0",
|
||||
"@dydxprotocol/v4-localization": "^0.0.25",
|
||||
"@ethersproject/providers": "^5.7.2",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@0xsquid/sdk':
|
||||
specifier: ^1.7.2
|
||||
@@ -23,8 +27,8 @@ dependencies:
|
||||
specifier: ^0.31.0
|
||||
version: 0.31.0
|
||||
'@dydxprotocol/abacus':
|
||||
specifier: ^0.4.1
|
||||
version: 0.4.1
|
||||
specifier: ^0.4.6
|
||||
version: 0.4.6
|
||||
'@dydxprotocol/v4-client-js':
|
||||
specifier: ^0.32.0
|
||||
version: 0.32.0
|
||||
@@ -1142,8 +1146,8 @@ packages:
|
||||
resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==}
|
||||
dev: true
|
||||
|
||||
/@dydxprotocol/abacus@0.4.1:
|
||||
resolution: {integrity: sha512-+2uKbTYBM6h7e3L8OX6gYwbqfIPtzwWUz4Onw6UCk7oD966Znwoky/PQmVDGjIcxw0BGWJUfHmF3Qd3t973tLQ==}
|
||||
/@dydxprotocol/abacus@0.4.6:
|
||||
resolution: {integrity: sha512-nvF9B1JuWzsnrPtQ/mKo06XWuqFc9fy6TEFcFwlIGGbxKortod7fSDj9sEZGKGdRyIj5wvl7H/Rgh+dlywO+aQ==}
|
||||
dev: false
|
||||
|
||||
/@dydxprotocol/dydxjs@0.3.0:
|
||||
@@ -15858,7 +15862,3 @@ packages:
|
||||
release-it: 15.11.0
|
||||
semver: 7.5.1
|
||||
dev: false
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 817 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 973 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
@@ -192,7 +192,9 @@ Styled.NotificationsToastArea = styled(NotificationsToastArea)`
|
||||
`;
|
||||
|
||||
Styled.DialogArea = styled.aside`
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
height: 100vh;
|
||||
z-index: 1;
|
||||
inset: 0;
|
||||
overflow: clip;
|
||||
${layoutMixins.noPointerEvents}
|
||||
|
||||
@@ -67,6 +67,7 @@ export enum AnalyticsEvent {
|
||||
// Onboarding
|
||||
OnboardingStepChanged = 'OnboardingStepChanged',
|
||||
OnboardingAccountDerived = 'OnboardingAccountDerived',
|
||||
OnboardingWalletIsNonDeterministic = 'OnboardingWalletIsNonDeterministic',
|
||||
|
||||
// Transfers
|
||||
TransferFaucet = 'TransferFaucet',
|
||||
|
||||
@@ -88,17 +88,17 @@ export type TooltipStrings = {
|
||||
};
|
||||
|
||||
export const ORDER_ERROR_CODE_MAP: { [key: number]: string } = {
|
||||
2000: '2000_FILL_OR_KILL_ORDER_COULD_NOT_BE_FULLY_FILLED',
|
||||
2001: '2001_REDUCE_ONLY_WOULD_INCREASE_POSITION_SIZE',
|
||||
2002: '2002_REDUCE_ONLY_WOULD_CHANGE_POSITION_SIDE',
|
||||
2003: '2003_POST_ONLY_WOULD_CROSS_MAKER_ORDER',
|
||||
3000: '3000_INVALID_ORDER_FLAGS',
|
||||
3001: '3001_INVALID_STATEFUL_ORDER_GOOD_TIL_BLOCK_TIME',
|
||||
3002: '3002_STATEFUL_ORDERS_CANNOT_REQUIRE_IMMEDIATE_EXECUTION',
|
||||
3003: '3003_TIME_EXCEEDS_GOOD_TIL_BLOCK_TIME',
|
||||
3004: '3004_GOOD_TIL_BLOCK_TIME_EXCEEDS_STATEFUL_ORDER_TIME_WINDOW',
|
||||
3005: '3005_STATEFUL_ORDER_ALREADY_EXISTS',
|
||||
3006: '3006_STATEFUL_ORDER_DOES_NOT_EXIST',
|
||||
3007: '3007_STATEFUL_ORDER_COLLATERALIZATION_CHECK_FAILED',
|
||||
3008: '3008_STATEFUL_ORDER_PREVIOUSLY_CANCELLED',
|
||||
2000: STRING_KEYS['2000_FILL_OR_KILL_ORDER_COULD_NOT_BE_FULLY_FILLED'],
|
||||
2001: STRING_KEYS['2001_REDUCE_ONLY_WOULD_INCREASE_POSITION_SIZE'],
|
||||
2002: STRING_KEYS['2002_REDUCE_ONLY_WOULD_CHANGE_POSITION_SIDE'],
|
||||
2003: STRING_KEYS['2003_POST_ONLY_WOULD_CROSS_MAKER_ORDER'],
|
||||
3000: STRING_KEYS['3000_INVALID_ORDER_FLAGS'],
|
||||
3001: STRING_KEYS['3001_INVALID_STATEFUL_ORDER_GOOD_TIL_BLOCK_TIME'],
|
||||
3002: STRING_KEYS['3002_STATEFUL_ORDERS_CANNOT_REQUIRE_IMMEDIATE_EXECUTION'],
|
||||
3003: STRING_KEYS['3003_TIME_EXCEEDS_GOOD_TIL_BLOCK_TIME'],
|
||||
3004: STRING_KEYS['3004_GOOD_TIL_BLOCK_TIME_EXCEEDS_STATEFUL_ORDER_TIME_WINDOW'],
|
||||
3005: STRING_KEYS['3005_STATEFUL_ORDER_ALREADY_EXISTS'],
|
||||
3006: STRING_KEYS['3006_STATEFUL_ORDER_DOES_NOT_EXIST'],
|
||||
3007: STRING_KEYS['3007_STATEFUL_ORDER_COLLATERALIZATION_CHECK_FAILED'],
|
||||
3008: STRING_KEYS['3008_STATEFUL_ORDER_PREVIOUSLY_CANCELLED'],
|
||||
};
|
||||
|
||||
@@ -32,6 +32,18 @@ export enum WalletConnectionType {
|
||||
WalletConnect2 = 'walletConnect2',
|
||||
}
|
||||
|
||||
export enum WalletErrorType {
|
||||
// General
|
||||
ChainMismatch,
|
||||
UserCanceled,
|
||||
|
||||
// Non-Deterministic
|
||||
NonDeterministicWallet,
|
||||
|
||||
// Misc
|
||||
Unknown,
|
||||
}
|
||||
|
||||
type WalletConnectionTypeConfig = {
|
||||
name: string;
|
||||
wagmiConnectorId?: string;
|
||||
|
||||
@@ -57,8 +57,8 @@ const useDydxClientContext = () => {
|
||||
networkConfig?.indexerSocketUrl &&
|
||||
networkConfig?.validatorUrl
|
||||
) {
|
||||
setCompositeClient(
|
||||
await CompositeClient.connect(
|
||||
try {
|
||||
const initializedClient = await CompositeClient.connect(
|
||||
new Network(
|
||||
selectedNetwork,
|
||||
new IndexerConfig(networkConfig.indexerUrl, networkConfig.indexerSocketUrl),
|
||||
@@ -67,8 +67,11 @@ const useDydxClientContext = () => {
|
||||
broadcastTimeoutMs: 60_000,
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
);
|
||||
setCompositeClient(initializedClient);
|
||||
} catch (error) {
|
||||
log('useDydxClient/initializeCompositeClient', error);
|
||||
}
|
||||
} else {
|
||||
setCompositeClient(undefined);
|
||||
}
|
||||
|
||||
@@ -551,7 +551,8 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
||||
} as HumanReadablePlaceOrderPayload & { isClosePosition: boolean });
|
||||
onSuccess?.();
|
||||
} catch (error) {
|
||||
const errorStringKey = error?.code && ORDER_ERROR_CODE_MAP[error.code as number];
|
||||
const errorCode: number | undefined = error?.code;
|
||||
const errorStringKey = errorCode ? ORDER_ERROR_CODE_MAP[errorCode] : undefined;
|
||||
onError?.({ errorStringKey });
|
||||
|
||||
log('useSubaccount/placeOrder', error, {
|
||||
|
||||
@@ -11,6 +11,8 @@ import {
|
||||
DYDX_CHAIN_INFO,
|
||||
} from '@/constants/wallets';
|
||||
|
||||
import { useLocalStorage } from '@/hooks/useLocalStorage';
|
||||
|
||||
import {
|
||||
useConnect as useConnectWagmi,
|
||||
useAccount as useAccountWagmi,
|
||||
@@ -26,10 +28,9 @@ import {
|
||||
} from 'graz';
|
||||
|
||||
import { resolveWagmiConnector } from '@/lib/wagmi';
|
||||
import { getWalletConnection } from '@/lib/wallet';
|
||||
import { getWalletConnection, parseWalletError } from '@/lib/wallet';
|
||||
import { log } from '@/lib/telemetry';
|
||||
|
||||
import { useLocalStorage } from '@/hooks/useLocalStorage';
|
||||
import { useStringGetter } from './useStringGetter';
|
||||
|
||||
export const useWalletConnection = () => {
|
||||
@@ -129,8 +130,6 @@ export const useWalletConnection = () => {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
log('useWalletConnection/connectWallet', error);
|
||||
|
||||
throw Object.assign(
|
||||
new Error([error.message, error.cause?.message].filter(Boolean).join('\n')),
|
||||
{
|
||||
@@ -158,7 +157,7 @@ export const useWalletConnection = () => {
|
||||
// Wallet selection
|
||||
|
||||
const [selectedWalletType, setSelectedWalletType] = useState<WalletType | undefined>(walletType);
|
||||
const [selectedWalletError, setSelectedWalletError] = useState<Error>();
|
||||
const [selectedWalletError, setSelectedWalletError] = useState<string>();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -173,8 +172,15 @@ export const useWalletConnection = () => {
|
||||
setWalletType(walletType);
|
||||
setWalletConnectionType(walletConnectionType);
|
||||
} catch (error) {
|
||||
log('useWalletConnection/connectWallet', error);
|
||||
setSelectedWalletError(error);
|
||||
const { walletErrorType, message } = parseWalletError({
|
||||
error,
|
||||
stringGetter,
|
||||
});
|
||||
|
||||
if (message) {
|
||||
log('useWalletConnection/connectWallet', error, { walletErrorType });
|
||||
setSelectedWalletError(message);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setWalletType(undefined);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { STRING_KEYS, StringGetterFunction } from '@/constants/localization';
|
||||
|
||||
import {
|
||||
type WalletConnection,
|
||||
wallets,
|
||||
WalletConnectionType,
|
||||
WalletErrorType,
|
||||
WalletType,
|
||||
} from '@/constants/wallets';
|
||||
|
||||
@@ -68,3 +71,66 @@ export const getWalletConnection = ({
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getWalletErrorType = ({ error }: { error: Error }) => {
|
||||
const { message } = error;
|
||||
const messageLower = message.toLowerCase();
|
||||
|
||||
// General - Cancelled
|
||||
if (
|
||||
messageLower.includes('connection request reset') ||
|
||||
messageLower.includes('rejected') ||
|
||||
messageLower.includes('reject') ||
|
||||
messageLower.includes('cancelled') ||
|
||||
messageLower.includes('canceled') ||
|
||||
messageLower.includes('user denied')
|
||||
) {
|
||||
return WalletErrorType.UserCanceled;
|
||||
}
|
||||
|
||||
if (messageLower.includes('chain mismatch')) {
|
||||
return WalletErrorType.ChainMismatch;
|
||||
}
|
||||
|
||||
// ImToken - User canceled
|
||||
if (messageLower.includes('用户取消了操作')) {
|
||||
return WalletErrorType.UserCanceled;
|
||||
}
|
||||
|
||||
if (messageLower.includes('does not support deterministic signing')) {
|
||||
return WalletErrorType.NonDeterministicWallet;
|
||||
}
|
||||
|
||||
return WalletErrorType.Unknown;
|
||||
};
|
||||
|
||||
export const parseWalletError = ({
|
||||
error,
|
||||
stringGetter,
|
||||
}: {
|
||||
error: Error;
|
||||
stringGetter: StringGetterFunction;
|
||||
}) => {
|
||||
const walletErrorType = getWalletErrorType({ error });
|
||||
let message;
|
||||
|
||||
switch (walletErrorType) {
|
||||
case WalletErrorType.ChainMismatch:
|
||||
case WalletErrorType.UserCanceled: {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
message = stringGetter({
|
||||
key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE,
|
||||
params: {
|
||||
ERROR_MESSAGE: error.message || stringGetter({ key: STRING_KEYS.UNKNOWN_ERROR }),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
walletErrorType,
|
||||
message,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -32,8 +32,12 @@ export const ChooseWallet = () => {
|
||||
<>
|
||||
{selectedWalletType && selectedWalletError && (
|
||||
<Styled.AlertMessage type={AlertType.Error}>
|
||||
{<h4>Couldn't connect to {stringGetter({ key: wallets[selectedWalletType].stringKey })}.</h4>}
|
||||
<code>{selectedWalletError.message}</code>
|
||||
{
|
||||
<h4>
|
||||
Couldn't connect to {stringGetter({ key: wallets[selectedWalletType].stringKey })}.
|
||||
</h4>
|
||||
}
|
||||
{selectedWalletError}
|
||||
</Styled.AlertMessage>
|
||||
)}
|
||||
|
||||
|
||||
@@ -25,11 +25,12 @@ import { Switch } from '@/components/Switch';
|
||||
import { WithReceipt } from '@/components/WithReceipt';
|
||||
import { WithTooltip } from '@/components/WithTooltip';
|
||||
|
||||
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||
|
||||
import { track } from '@/lib/analytics';
|
||||
import { isTruthy } from '@/lib/isTruthy';
|
||||
import { log } from '@/lib/telemetry';
|
||||
|
||||
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||
import { parseWalletError } from '@/lib/wallet';
|
||||
|
||||
type ElementProps = {
|
||||
status: EvmDerivedAccountStatus;
|
||||
@@ -66,8 +67,12 @@ export const GenerateKeys = ({
|
||||
try {
|
||||
await matchNetwork?.();
|
||||
} catch (error) {
|
||||
setError(error.message);
|
||||
log('GenerateKeys/switchNetwork', error);
|
||||
const { message, walletErrorType } = parseWalletError({ error, stringGetter });
|
||||
|
||||
if (message) {
|
||||
log('GenerateKeys/switchNetwork', error, { walletErrorType });
|
||||
setError(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -124,7 +129,12 @@ export const GenerateKeys = ({
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
log('GenerateKeys/getSubaccounts', error);
|
||||
const { message } = parseWalletError({ error, stringGetter });
|
||||
|
||||
if (message) {
|
||||
track(AnalyticsEvent.OnboardingWalletIsNonDeterministic);
|
||||
setError(message);
|
||||
}
|
||||
}
|
||||
|
||||
await setWalletFromEvmSignature(signature);
|
||||
@@ -140,48 +150,48 @@ export const GenerateKeys = ({
|
||||
setStatus(EvmDerivedAccountStatus.Derived);
|
||||
} catch (error) {
|
||||
setStatus(EvmDerivedAccountStatus.NotDerived);
|
||||
setError(error?.message);
|
||||
const { message, walletErrorType } = parseWalletError({ error, stringGetter });
|
||||
|
||||
log('GenerateKeys/deriveKeys', error);
|
||||
|
||||
throw error;
|
||||
if (message) {
|
||||
setError(message);
|
||||
log('GenerateKeys/deriveKeys', error, { walletErrorType });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{isMobile && (
|
||||
<Styled.MobileStatusCards>
|
||||
{[
|
||||
{
|
||||
status: EvmDerivedAccountStatus.Deriving,
|
||||
title: stringGetter({ key: STRING_KEYS.GENERATE_COSMOS_WALLET }),
|
||||
description: stringGetter({ key: STRING_KEYS.GENERATE_COSMOS_WALLET }),
|
||||
},
|
||||
status === EvmDerivedAccountStatus.EnsuringDeterminism && {
|
||||
status: EvmDerivedAccountStatus.EnsuringDeterminism,
|
||||
title: stringGetter({ key: STRING_KEYS.VERIFY_WALLET_COMPATIBILITY }),
|
||||
description: stringGetter({ key: STRING_KEYS.ENSURES_WALLET_SUPPORT }),
|
||||
},
|
||||
]
|
||||
.filter(isTruthy)
|
||||
.map((step) => (
|
||||
<Styled.StatusCard key={step.status} active={status === step.status}>
|
||||
{status < step.status ? (
|
||||
<LoadingSpinner disabled />
|
||||
) : status === step.status ? (
|
||||
<LoadingSpinner />
|
||||
) : (
|
||||
<Styled.GreenCheckCircle />
|
||||
)}
|
||||
<div>
|
||||
<h3>{step.title}</h3>
|
||||
<p>{step.description}</p>
|
||||
</div>
|
||||
</Styled.StatusCard>
|
||||
))}
|
||||
</Styled.MobileStatusCards>
|
||||
)}
|
||||
<Styled.StatusCardsContainer>
|
||||
{[
|
||||
{
|
||||
status: EvmDerivedAccountStatus.Deriving,
|
||||
title: stringGetter({ key: STRING_KEYS.GENERATE_COSMOS_WALLET }),
|
||||
description: stringGetter({ key: STRING_KEYS.GENERATE_COSMOS_WALLET }),
|
||||
},
|
||||
status === EvmDerivedAccountStatus.EnsuringDeterminism && {
|
||||
status: EvmDerivedAccountStatus.EnsuringDeterminism,
|
||||
title: stringGetter({ key: STRING_KEYS.VERIFY_WALLET_COMPATIBILITY }),
|
||||
description: stringGetter({ key: STRING_KEYS.ENSURES_WALLET_SUPPORT }),
|
||||
},
|
||||
]
|
||||
.filter(isTruthy)
|
||||
.map((step) => (
|
||||
<Styled.StatusCard key={step.status} active={status === step.status}>
|
||||
{status < step.status ? (
|
||||
<LoadingSpinner disabled />
|
||||
) : status === step.status ? (
|
||||
<LoadingSpinner />
|
||||
) : (
|
||||
<Styled.GreenCheckCircle />
|
||||
)}
|
||||
<div>
|
||||
<h3>{step.title}</h3>
|
||||
<p>{step.description}</p>
|
||||
</div>
|
||||
</Styled.StatusCard>
|
||||
))}
|
||||
</Styled.StatusCardsContainer>
|
||||
|
||||
<Styled.Footer>
|
||||
<Styled.RememberMe htmlFor="remember-me">
|
||||
<WithTooltip withIcon tooltip="remember-me">
|
||||
@@ -251,7 +261,7 @@ export const GenerateKeys = ({
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.MobileStatusCards = styled.div`
|
||||
Styled.StatusCardsContainer = styled.div`
|
||||
display: grid;
|
||||
margin-top: 1rem;
|
||||
gap: 1rem;
|
||||
|
||||