fix(withdraws): clear eth network errors (#4044)
This commit is contained in:
parent
b6286cd0f3
commit
e47b868c53
@ -13,3 +13,4 @@ export * from './lib/remove-0x';
|
|||||||
export * from './lib/remove-pagination-wrapper';
|
export * from './lib/remove-pagination-wrapper';
|
||||||
export * from './lib/time';
|
export * from './lib/time';
|
||||||
export * from './lib/validate';
|
export * from './lib/validate';
|
||||||
|
export * from './lib/resolve-network-name';
|
||||||
|
6
libs/utils/src/lib/resolve-network-name.ts
Normal file
6
libs/utils/src/lib/resolve-network-name.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
const NETWORK_NAME_MAP: Readonly<Record<string, string>> = {
|
||||||
|
'1': 'Ethereum Mainnet',
|
||||||
|
'11155111': 'Sepolia test network',
|
||||||
|
};
|
||||||
|
export const resolveNetworkName = (chainId?: string): string =>
|
||||||
|
NETWORK_NAME_MAP[chainId || ''] || `(chainID: ${chainId})`;
|
@ -9,7 +9,10 @@ import { Intent } from '@vegaprotocol/ui-toolkit';
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import compact from 'lodash/compact';
|
import compact from 'lodash/compact';
|
||||||
import type { EthWithdrawalApprovalState } from './use-ethereum-withdraw-approvals-store';
|
import type { EthWithdrawalApprovalState } from './use-ethereum-withdraw-approvals-store';
|
||||||
import { useEthWithdrawApprovalsStore } from './use-ethereum-withdraw-approvals-store';
|
import {
|
||||||
|
useEthWithdrawApprovalsStore,
|
||||||
|
WithdrawalFailure,
|
||||||
|
} from './use-ethereum-withdraw-approvals-store';
|
||||||
import { ApprovalStatus } from './use-ethereum-withdraw-approvals-store';
|
import { ApprovalStatus } from './use-ethereum-withdraw-approvals-store';
|
||||||
import { VerificationStatus } from './withdrawal-approval-status';
|
import { VerificationStatus } from './withdrawal-approval-status';
|
||||||
|
|
||||||
@ -26,12 +29,23 @@ const EthWithdrawalApprovalToastContent = ({
|
|||||||
}: {
|
}: {
|
||||||
tx: EthWithdrawalApprovalState;
|
tx: EthWithdrawalApprovalState;
|
||||||
}) => {
|
}) => {
|
||||||
|
const isConnectionFailure =
|
||||||
|
tx.failureReason &&
|
||||||
|
[
|
||||||
|
WithdrawalFailure.WrongConnection,
|
||||||
|
WithdrawalFailure.NoConnection,
|
||||||
|
].includes(tx.failureReason);
|
||||||
|
|
||||||
let title = '';
|
let title = '';
|
||||||
if (tx.status === ApprovalStatus.Error) {
|
if (tx.status === ApprovalStatus.Error) {
|
||||||
title = t('Error occurred');
|
title = t('Error occurred');
|
||||||
}
|
}
|
||||||
if (tx.status === ApprovalStatus.Pending) {
|
if (tx.status === ApprovalStatus.Pending) {
|
||||||
title = t('Pending approval');
|
if (tx.failureReason && isConnectionFailure) {
|
||||||
|
title = tx.message || t('Withdraw failure');
|
||||||
|
} else {
|
||||||
|
title = t('Pending approval');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (tx.status === ApprovalStatus.Delayed) {
|
if (tx.status === ApprovalStatus.Delayed) {
|
||||||
title = t('Delayed');
|
title = t('Delayed');
|
||||||
@ -39,11 +53,12 @@ const EthWithdrawalApprovalToastContent = ({
|
|||||||
if (tx.status === ApprovalStatus.Ready) {
|
if (tx.status === ApprovalStatus.Ready) {
|
||||||
title = t('Approved');
|
title = t('Approved');
|
||||||
}
|
}
|
||||||
|
|
||||||
const num = formatNumber(
|
const num = formatNumber(
|
||||||
toBigNum(tx.withdrawal.amount, tx.withdrawal.asset.decimals),
|
toBigNum(tx.withdrawal.amount, tx.withdrawal.asset.decimals),
|
||||||
tx.withdrawal.asset.decimals
|
tx.withdrawal.asset.decimals
|
||||||
);
|
);
|
||||||
const details = (
|
const details = isConnectionFailure ? null : (
|
||||||
<Panel>
|
<Panel>
|
||||||
<strong>
|
<strong>
|
||||||
{t('Withdraw')} {num} {tx.withdrawal.asset.symbol}
|
{t('Withdraw')} {num} {tx.withdrawal.asset.symbol}
|
||||||
@ -61,7 +76,8 @@ const EthWithdrawalApprovalToastContent = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isFinal = (tx: EthWithdrawalApprovalState) =>
|
const isFinal = (tx: EthWithdrawalApprovalState) =>
|
||||||
[ApprovalStatus.Ready, ApprovalStatus.Error].includes(tx.status);
|
[ApprovalStatus.Ready, ApprovalStatus.Error].includes(tx.status) &&
|
||||||
|
!tx.failureReason;
|
||||||
|
|
||||||
export const useEthereumWithdrawApprovalsToasts = () => {
|
export const useEthereumWithdrawApprovalsToasts = () => {
|
||||||
const [setToast, remove] = useToasts((state) => [
|
const [setToast, remove] = useToasts((state) => [
|
||||||
@ -74,21 +90,34 @@ export const useEthereumWithdrawApprovalsToasts = () => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const fromWithdrawalApproval = useCallback(
|
const fromWithdrawalApproval = useCallback(
|
||||||
(tx: EthWithdrawalApprovalState): Toast => ({
|
(tx: EthWithdrawalApprovalState): Toast => {
|
||||||
id: `withdrawal-${tx.id}`,
|
const loader =
|
||||||
intent: intentMap[tx.status],
|
tx.status === ApprovalStatus.Pending &&
|
||||||
onClose: () => {
|
!(
|
||||||
if ([ApprovalStatus.Error, ApprovalStatus.Ready].includes(tx.status)) {
|
tx.failureReason &&
|
||||||
deleteTx(tx.id);
|
[
|
||||||
} else {
|
WithdrawalFailure.WrongConnection,
|
||||||
dismissTx(tx.id);
|
WithdrawalFailure.NoConnection,
|
||||||
}
|
].includes(tx.failureReason)
|
||||||
remove(`withdrawal-${tx.id}`);
|
);
|
||||||
},
|
return {
|
||||||
loader: tx.status === ApprovalStatus.Pending,
|
id: `withdrawal-${tx.id}`,
|
||||||
content: <EthWithdrawalApprovalToastContent tx={tx} />,
|
intent: intentMap[tx.status],
|
||||||
closeAfter: isFinal(tx) ? CLOSE_AFTER : undefined,
|
onClose: () => {
|
||||||
}),
|
if (
|
||||||
|
[ApprovalStatus.Error, ApprovalStatus.Ready].includes(tx.status)
|
||||||
|
) {
|
||||||
|
deleteTx(tx.id);
|
||||||
|
} else {
|
||||||
|
dismissTx(tx.id);
|
||||||
|
}
|
||||||
|
remove(`withdrawal-${tx.id}`);
|
||||||
|
},
|
||||||
|
loader,
|
||||||
|
content: <EthWithdrawalApprovalToastContent tx={tx} />,
|
||||||
|
closeAfter: isFinal(tx) ? CLOSE_AFTER : undefined,
|
||||||
|
};
|
||||||
|
},
|
||||||
[deleteTx, dismissTx, remove]
|
[deleteTx, dismissTx, remove]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -5,7 +5,10 @@ import type { ReactNode } from 'react';
|
|||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
import waitForNextTick from 'flush-promises';
|
import waitForNextTick from 'flush-promises';
|
||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import { ApprovalStatus } from './use-ethereum-withdraw-approvals-store';
|
import {
|
||||||
|
ApprovalStatus,
|
||||||
|
WithdrawalFailure,
|
||||||
|
} from './use-ethereum-withdraw-approvals-store';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import type {
|
import type {
|
||||||
EthWithdrawApprovalStore,
|
EthWithdrawApprovalStore,
|
||||||
@ -21,7 +24,7 @@ import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
|||||||
|
|
||||||
const mockWeb3Provider = jest.fn();
|
const mockWeb3Provider = jest.fn();
|
||||||
|
|
||||||
let mockChainId = 111111;
|
let mockChainId: number | undefined = 111111;
|
||||||
jest.mock('@web3-react/core', () => ({
|
jest.mock('@web3-react/core', () => ({
|
||||||
useWeb3React: () => ({
|
useWeb3React: () => ({
|
||||||
provider: mockWeb3Provider(),
|
provider: mockWeb3Provider(),
|
||||||
@ -314,9 +317,29 @@ describe('useEthWithdrawApprovalsManager', () => {
|
|||||||
update,
|
update,
|
||||||
});
|
});
|
||||||
render();
|
render();
|
||||||
expect(update.mock.calls[0][1].status).toEqual(ApprovalStatus.Error);
|
expect(update.mock.calls[0][1].status).toEqual(ApprovalStatus.Pending);
|
||||||
|
expect(update.mock.calls[0][1].message).toEqual('Change network');
|
||||||
|
expect(update.mock.calls[0][1].failureReason).toEqual(
|
||||||
|
WithdrawalFailure.WrongConnection
|
||||||
|
);
|
||||||
|
mockChainId = 111111;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detect no chainId', () => {
|
||||||
|
mockChainId = undefined;
|
||||||
|
const transaction = createWithdrawTransaction();
|
||||||
|
mockEthTransactionStoreState.mockReturnValue({ create });
|
||||||
|
mockEthWithdrawApprovalsStoreState.mockReturnValue({
|
||||||
|
transactions: [transaction],
|
||||||
|
update,
|
||||||
|
});
|
||||||
|
render();
|
||||||
|
expect(update.mock.calls[0][1].status).toEqual(ApprovalStatus.Pending);
|
||||||
expect(update.mock.calls[0][1].message).toEqual(
|
expect(update.mock.calls[0][1].message).toEqual(
|
||||||
'You are on the wrong network'
|
'Connect wallet to withdraw'
|
||||||
|
);
|
||||||
|
expect(update.mock.calls[0][1].failureReason).toEqual(
|
||||||
|
WithdrawalFailure.NoConnection
|
||||||
);
|
);
|
||||||
mockChainId = 111111;
|
mockChainId = 111111;
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useApolloClient } from '@apollo/client';
|
import { useApolloClient } from '@apollo/client';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import { useRef, useEffect } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { addDecimal } from '@vegaprotocol/utils';
|
import { addDecimal } from '@vegaprotocol/utils';
|
||||||
import { useGetWithdrawThreshold } from './use-get-withdraw-threshold';
|
import { useGetWithdrawThreshold } from './use-get-withdraw-threshold';
|
||||||
import { useGetWithdrawDelay } from './use-get-withdraw-delay';
|
import { useGetWithdrawDelay } from './use-get-withdraw-delay';
|
||||||
@ -21,8 +21,9 @@ import { WithdrawalApprovalDocument } from '@vegaprotocol/wallet';
|
|||||||
|
|
||||||
import { useEthTransactionStore } from './use-ethereum-transaction-store';
|
import { useEthTransactionStore } from './use-ethereum-transaction-store';
|
||||||
import {
|
import {
|
||||||
useEthWithdrawApprovalsStore,
|
|
||||||
ApprovalStatus,
|
ApprovalStatus,
|
||||||
|
useEthWithdrawApprovalsStore,
|
||||||
|
WithdrawalFailure,
|
||||||
} from './use-ethereum-withdraw-approvals-store';
|
} from './use-ethereum-withdraw-approvals-store';
|
||||||
|
|
||||||
export const useEthWithdrawApprovalsManager = () => {
|
export const useEthWithdrawApprovalsManager = () => {
|
||||||
@ -55,16 +56,27 @@ export const useEthWithdrawApprovalsManager = () => {
|
|||||||
message: t(
|
message: t(
|
||||||
`Invalid asset source: ${withdrawal.asset.source.__typename}`
|
`Invalid asset source: ${withdrawal.asset.source.__typename}`
|
||||||
),
|
),
|
||||||
|
failureReason: WithdrawalFailure.InvalidAsset,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!chainId) {
|
||||||
|
update(transaction.id, {
|
||||||
|
status: ApprovalStatus.Pending,
|
||||||
|
message: t(`Connect wallet to withdraw`),
|
||||||
|
failureReason: WithdrawalFailure.NoConnection,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (chainId?.toString() !== config?.chain_id) {
|
if (chainId?.toString() !== config?.chain_id) {
|
||||||
update(transaction.id, {
|
update(transaction.id, {
|
||||||
status: ApprovalStatus.Error,
|
status: ApprovalStatus.Pending,
|
||||||
message: t(`You are on the wrong network`),
|
message: t(`Change network`),
|
||||||
|
failureReason: WithdrawalFailure.WrongConnection,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
update(transaction.id, {
|
update(transaction.id, {
|
||||||
status: ApprovalStatus.Pending,
|
status: ApprovalStatus.Pending,
|
||||||
message: t('Verifying withdrawal approval'),
|
message: t('Verifying withdrawal approval'),
|
||||||
|
@ -14,6 +14,13 @@ export enum ApprovalStatus {
|
|||||||
Error = 'Error',
|
Error = 'Error',
|
||||||
Ready = 'Ready',
|
Ready = 'Ready',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum WithdrawalFailure {
|
||||||
|
InvalidAsset,
|
||||||
|
NoConnection,
|
||||||
|
WrongConnection,
|
||||||
|
}
|
||||||
|
|
||||||
export interface EthWithdrawalApprovalState {
|
export interface EthWithdrawalApprovalState {
|
||||||
id: number;
|
id: number;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
@ -24,6 +31,7 @@ export interface EthWithdrawalApprovalState {
|
|||||||
dialogOpen?: boolean;
|
dialogOpen?: boolean;
|
||||||
withdrawal: WithdrawalBusEventFieldsFragment;
|
withdrawal: WithdrawalBusEventFieldsFragment;
|
||||||
approval?: WithdrawalApprovalQuery['erc20WithdrawalApproval'];
|
approval?: WithdrawalApprovalQuery['erc20WithdrawalApproval'];
|
||||||
|
failureReason?: WithdrawalFailure;
|
||||||
}
|
}
|
||||||
export interface EthWithdrawApprovalStore {
|
export interface EthWithdrawApprovalStore {
|
||||||
transactions: (EthWithdrawalApprovalState | undefined)[];
|
transactions: (EthWithdrawalApprovalState | undefined)[];
|
||||||
@ -42,6 +50,7 @@ export interface EthWithdrawApprovalStore {
|
|||||||
| 'threshold'
|
| 'threshold'
|
||||||
| 'completeTimestamp'
|
| 'completeTimestamp'
|
||||||
| 'dialogOpen'
|
| 'dialogOpen'
|
||||||
|
| 'failureReason'
|
||||||
>
|
>
|
||||||
>
|
>
|
||||||
) => void;
|
) => void;
|
||||||
@ -86,6 +95,7 @@ export const useEthWithdrawApprovalsStore = create<EthWithdrawApprovalStore>()(
|
|||||||
| 'threshold'
|
| 'threshold'
|
||||||
| 'completeTimestamp'
|
| 'completeTimestamp'
|
||||||
| 'dialogOpen'
|
| 'dialogOpen'
|
||||||
|
| 'failureReason'
|
||||||
>
|
>
|
||||||
>
|
>
|
||||||
) =>
|
) =>
|
||||||
|
@ -1,17 +1,69 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
import {
|
||||||
|
getDateTimeFormat,
|
||||||
|
resolveNetworkName,
|
||||||
|
truncateByChars,
|
||||||
|
} from '@vegaprotocol/utils';
|
||||||
import type { EthWithdrawalApprovalState } from './use-ethereum-withdraw-approvals-store';
|
import type { EthWithdrawalApprovalState } from './use-ethereum-withdraw-approvals-store';
|
||||||
import { ApprovalStatus } from './use-ethereum-withdraw-approvals-store';
|
import {
|
||||||
|
ApprovalStatus,
|
||||||
|
useEthWithdrawApprovalsStore,
|
||||||
|
WithdrawalFailure,
|
||||||
|
} from './use-ethereum-withdraw-approvals-store';
|
||||||
|
import { useEthereumConfig } from './use-ethereum-config';
|
||||||
|
import { Button, useToasts } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { useWeb3ConnectStore } from './web3-connect-store';
|
||||||
|
|
||||||
export const VerificationStatus = ({
|
export const VerificationStatus = ({
|
||||||
state,
|
state,
|
||||||
}: {
|
}: {
|
||||||
state: EthWithdrawalApprovalState;
|
state: EthWithdrawalApprovalState;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { config } = useEthereumConfig();
|
||||||
|
const openDialog = useWeb3ConnectStore((state) => state.open);
|
||||||
|
const remove = useToasts((state) => state.remove);
|
||||||
|
const deleteTx = useEthWithdrawApprovalsStore((state) => state.delete);
|
||||||
|
|
||||||
if (state.status === ApprovalStatus.Error) {
|
if (state.status === ApprovalStatus.Error) {
|
||||||
return <p>{state.message || t('Something went wrong')}</p>;
|
return <p>{state.message || t('Something went wrong')}</p>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
state.failureReason &&
|
||||||
|
[
|
||||||
|
WithdrawalFailure.WrongConnection,
|
||||||
|
WithdrawalFailure.NoConnection,
|
||||||
|
].includes(state.failureReason)
|
||||||
|
) {
|
||||||
|
return state.failureReason === WithdrawalFailure.NoConnection ? (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
{t('To complete this withdrawal, connect the Ethereum wallet %s', [
|
||||||
|
truncateByChars(state.withdrawal.details?.receiverAddress || ' '),
|
||||||
|
])}
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
openDialog();
|
||||||
|
deleteTx(state.id);
|
||||||
|
remove(`withdrawal-${state.id}`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Connect wallet')}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<p>{t('Your Ethereum wallet is connected to the wrong network.')}</p>
|
||||||
|
<p className="mt-2">
|
||||||
|
{t('Go to your Ethereum wallet and connect to the network %s', [
|
||||||
|
resolveNetworkName(config?.chain_id),
|
||||||
|
])}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (state.status === ApprovalStatus.Pending) {
|
if (state.status === ApprovalStatus.Pending) {
|
||||||
return <p>{t('Verifying...')}</p>;
|
return <p>{t('Verifying...')}</p>;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user