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/time';
|
||||
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 compact from 'lodash/compact';
|
||||
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 { VerificationStatus } from './withdrawal-approval-status';
|
||||
|
||||
@ -26,12 +29,23 @@ const EthWithdrawalApprovalToastContent = ({
|
||||
}: {
|
||||
tx: EthWithdrawalApprovalState;
|
||||
}) => {
|
||||
const isConnectionFailure =
|
||||
tx.failureReason &&
|
||||
[
|
||||
WithdrawalFailure.WrongConnection,
|
||||
WithdrawalFailure.NoConnection,
|
||||
].includes(tx.failureReason);
|
||||
|
||||
let title = '';
|
||||
if (tx.status === ApprovalStatus.Error) {
|
||||
title = t('Error occurred');
|
||||
}
|
||||
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) {
|
||||
title = t('Delayed');
|
||||
@ -39,11 +53,12 @@ const EthWithdrawalApprovalToastContent = ({
|
||||
if (tx.status === ApprovalStatus.Ready) {
|
||||
title = t('Approved');
|
||||
}
|
||||
|
||||
const num = formatNumber(
|
||||
toBigNum(tx.withdrawal.amount, tx.withdrawal.asset.decimals),
|
||||
tx.withdrawal.asset.decimals
|
||||
);
|
||||
const details = (
|
||||
const details = isConnectionFailure ? null : (
|
||||
<Panel>
|
||||
<strong>
|
||||
{t('Withdraw')} {num} {tx.withdrawal.asset.symbol}
|
||||
@ -61,7 +76,8 @@ const EthWithdrawalApprovalToastContent = ({
|
||||
};
|
||||
|
||||
const isFinal = (tx: EthWithdrawalApprovalState) =>
|
||||
[ApprovalStatus.Ready, ApprovalStatus.Error].includes(tx.status);
|
||||
[ApprovalStatus.Ready, ApprovalStatus.Error].includes(tx.status) &&
|
||||
!tx.failureReason;
|
||||
|
||||
export const useEthereumWithdrawApprovalsToasts = () => {
|
||||
const [setToast, remove] = useToasts((state) => [
|
||||
@ -74,21 +90,34 @@ export const useEthereumWithdrawApprovalsToasts = () => {
|
||||
]);
|
||||
|
||||
const fromWithdrawalApproval = useCallback(
|
||||
(tx: EthWithdrawalApprovalState): Toast => ({
|
||||
id: `withdrawal-${tx.id}`,
|
||||
intent: intentMap[tx.status],
|
||||
onClose: () => {
|
||||
if ([ApprovalStatus.Error, ApprovalStatus.Ready].includes(tx.status)) {
|
||||
deleteTx(tx.id);
|
||||
} else {
|
||||
dismissTx(tx.id);
|
||||
}
|
||||
remove(`withdrawal-${tx.id}`);
|
||||
},
|
||||
loader: tx.status === ApprovalStatus.Pending,
|
||||
content: <EthWithdrawalApprovalToastContent tx={tx} />,
|
||||
closeAfter: isFinal(tx) ? CLOSE_AFTER : undefined,
|
||||
}),
|
||||
(tx: EthWithdrawalApprovalState): Toast => {
|
||||
const loader =
|
||||
tx.status === ApprovalStatus.Pending &&
|
||||
!(
|
||||
tx.failureReason &&
|
||||
[
|
||||
WithdrawalFailure.WrongConnection,
|
||||
WithdrawalFailure.NoConnection,
|
||||
].includes(tx.failureReason)
|
||||
);
|
||||
return {
|
||||
id: `withdrawal-${tx.id}`,
|
||||
intent: intentMap[tx.status],
|
||||
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]
|
||||
);
|
||||
|
||||
|
@ -5,7 +5,10 @@ import type { ReactNode } from 'react';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import waitForNextTick from 'flush-promises';
|
||||
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 type {
|
||||
EthWithdrawApprovalStore,
|
||||
@ -21,7 +24,7 @@ import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||
|
||||
const mockWeb3Provider = jest.fn();
|
||||
|
||||
let mockChainId = 111111;
|
||||
let mockChainId: number | undefined = 111111;
|
||||
jest.mock('@web3-react/core', () => ({
|
||||
useWeb3React: () => ({
|
||||
provider: mockWeb3Provider(),
|
||||
@ -314,9 +317,29 @@ describe('useEthWithdrawApprovalsManager', () => {
|
||||
update,
|
||||
});
|
||||
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(
|
||||
'You are on the wrong network'
|
||||
'Connect wallet to withdraw'
|
||||
);
|
||||
expect(update.mock.calls[0][1].failureReason).toEqual(
|
||||
WithdrawalFailure.NoConnection
|
||||
);
|
||||
mockChainId = 111111;
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { useRef, useEffect } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { addDecimal } from '@vegaprotocol/utils';
|
||||
import { useGetWithdrawThreshold } from './use-get-withdraw-threshold';
|
||||
import { useGetWithdrawDelay } from './use-get-withdraw-delay';
|
||||
@ -21,8 +21,9 @@ import { WithdrawalApprovalDocument } from '@vegaprotocol/wallet';
|
||||
|
||||
import { useEthTransactionStore } from './use-ethereum-transaction-store';
|
||||
import {
|
||||
useEthWithdrawApprovalsStore,
|
||||
ApprovalStatus,
|
||||
useEthWithdrawApprovalsStore,
|
||||
WithdrawalFailure,
|
||||
} from './use-ethereum-withdraw-approvals-store';
|
||||
|
||||
export const useEthWithdrawApprovalsManager = () => {
|
||||
@ -55,16 +56,27 @@ export const useEthWithdrawApprovalsManager = () => {
|
||||
message: t(
|
||||
`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;
|
||||
}
|
||||
if (chainId?.toString() !== config?.chain_id) {
|
||||
update(transaction.id, {
|
||||
status: ApprovalStatus.Error,
|
||||
message: t(`You are on the wrong network`),
|
||||
status: ApprovalStatus.Pending,
|
||||
message: t(`Change network`),
|
||||
failureReason: WithdrawalFailure.WrongConnection,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
update(transaction.id, {
|
||||
status: ApprovalStatus.Pending,
|
||||
message: t('Verifying withdrawal approval'),
|
||||
|
@ -14,6 +14,13 @@ export enum ApprovalStatus {
|
||||
Error = 'Error',
|
||||
Ready = 'Ready',
|
||||
}
|
||||
|
||||
export enum WithdrawalFailure {
|
||||
InvalidAsset,
|
||||
NoConnection,
|
||||
WrongConnection,
|
||||
}
|
||||
|
||||
export interface EthWithdrawalApprovalState {
|
||||
id: number;
|
||||
createdAt: Date;
|
||||
@ -24,6 +31,7 @@ export interface EthWithdrawalApprovalState {
|
||||
dialogOpen?: boolean;
|
||||
withdrawal: WithdrawalBusEventFieldsFragment;
|
||||
approval?: WithdrawalApprovalQuery['erc20WithdrawalApproval'];
|
||||
failureReason?: WithdrawalFailure;
|
||||
}
|
||||
export interface EthWithdrawApprovalStore {
|
||||
transactions: (EthWithdrawalApprovalState | undefined)[];
|
||||
@ -42,6 +50,7 @@ export interface EthWithdrawApprovalStore {
|
||||
| 'threshold'
|
||||
| 'completeTimestamp'
|
||||
| 'dialogOpen'
|
||||
| 'failureReason'
|
||||
>
|
||||
>
|
||||
) => void;
|
||||
@ -86,6 +95,7 @@ export const useEthWithdrawApprovalsStore = create<EthWithdrawApprovalStore>()(
|
||||
| 'threshold'
|
||||
| 'completeTimestamp'
|
||||
| 'dialogOpen'
|
||||
| 'failureReason'
|
||||
>
|
||||
>
|
||||
) =>
|
||||
|
@ -1,17 +1,69 @@
|
||||
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 { 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 = ({
|
||||
state,
|
||||
}: {
|
||||
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) {
|
||||
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) {
|
||||
return <p>{t('Verifying...')}</p>;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user