import type { ReactNode } from 'react'; import { useAssetsDataProvider } from '@vegaprotocol/assets'; import { ETHERSCAN_TX, useEtherscanLink } from '@vegaprotocol/environment'; import { formatNumber, toBigNum } from '@vegaprotocol/utils'; import { t } from '@vegaprotocol/i18n'; import type { Toast, ToastContent } from '@vegaprotocol/ui-toolkit'; import { ToastHeading } from '@vegaprotocol/ui-toolkit'; import { Panel } from '@vegaprotocol/ui-toolkit'; import { CLOSE_AFTER } from '@vegaprotocol/ui-toolkit'; import { useToasts } from '@vegaprotocol/ui-toolkit'; import { ExternalLink, Intent, ProgressBar } from '@vegaprotocol/ui-toolkit'; import { useCallback } from 'react'; import compact from 'lodash/compact'; import type { EthStoredTxState } from '@vegaprotocol/web3'; import { EthTxStatus, isEthereumError, TransactionContent, useEthTransactionStore, } from '@vegaprotocol/web3'; const intentMap: { [s in EthTxStatus]: Intent } = { Default: Intent.Primary, Requested: Intent.Warning, Pending: Intent.Warning, Error: Intent.Danger, Complete: Intent.Warning, Confirmed: Intent.Success, }; const isWithdrawTransaction = (tx: EthStoredTxState) => tx.methodName === 'withdraw_asset'; const isDepositTransaction = (tx: EthStoredTxState) => tx.methodName === 'deposit_asset'; const EthTransactionDetails = ({ tx }: { tx: EthStoredTxState }) => { const { data: assets } = useAssetsDataProvider(); if (!assets) return null; const isWithdraw = isWithdrawTransaction(tx); const isDeposit = isDepositTransaction(tx); let assetInfo: ReactNode; if ((isWithdraw || isDeposit) && tx.args.length > 2 && tx.assetId) { const asset = assets.find((a) => a.id === tx.assetId); if (asset) { let label = ''; if (isWithdraw) label = t('Withdraw'); if (isDeposit) label = t('Deposit'); assetInfo = ( {label}{' '} {formatNumber(toBigNum(tx.args[1], asset.decimals), asset.decimals)}{' '} {asset.symbol} ); } } if (assetInfo || tx.requiresConfirmation) { return ( {assetInfo} {tx.status === EthTxStatus.Pending && ( <>

{t('Awaiting confirmations')}{' '} {`(${tx.confirmations}/${tx.requiredConfirmations})`}

)}
); } return null; }; type EthTxToastContentProps = { tx: EthStoredTxState; }; const EthTxRequestedToastContent = ({ tx }: EthTxToastContentProps) => { return ( <> {t('Action required')}

{t( 'Please go to your wallet application and approve or reject the transaction.' )}

); }; const EthTxPendingToastContent = ({ tx }: EthTxToastContentProps) => { return ( <> {t('Awaiting confirmation')}

{t('Please wait for your transaction to be confirmed.')}

); }; const EthTxErrorToastContent = ({ tx }: EthTxToastContentProps) => { let errorMessage = ''; if (isEthereumError(tx.error)) { errorMessage = tx.error.reason; } else if (tx.error instanceof Error) { errorMessage = tx.error.message; } return ( <> {t('Error occurred')}

{errorMessage}

); }; const EtherscanLink = ({ tx }: EthTxToastContentProps) => { const etherscanLink = useEtherscanLink(); return tx.txHash ? (

{t('View on Etherscan')}

) : null; }; const EthTxConfirmedToastContent = ({ tx }: EthTxToastContentProps) => { return ( <> {t('Transaction confirmed')}

{t('Your transaction has been confirmed.')}

); }; const EthTxCompletedToastContent = ({ tx }: EthTxToastContentProps) => { const isDeposit = isDepositTransaction(tx); return ( <> {t('Processing')} {isDeposit && t('deposit')}

{t('Your transaction has been completed.')}{' '} {isDeposit && t('Waiting for deposit confirmation.')}

); }; const isFinal = (tx: EthStoredTxState) => [EthTxStatus.Confirmed, EthTxStatus.Error].includes(tx.status); export const useEthereumTransactionToasts = () => { const [setToast, removeToast] = useToasts((store) => [ store.setToast, store.remove, ]); const dismissTx = useEthTransactionStore((state) => state.dismiss); const onClose = useCallback( (tx: EthStoredTxState) => () => { dismissTx(tx.id); removeToast(`eth-${tx.id}`); }, [dismissTx, removeToast] ); const fromEthTransaction = useCallback( (tx: EthStoredTxState): Toast => { let content: ToastContent = ; const closeAfter = isFinal(tx) ? CLOSE_AFTER : undefined; if (tx.status === EthTxStatus.Requested) { content = ; } if (tx.status === EthTxStatus.Pending) { content = ; } if (tx.status === EthTxStatus.Complete) { content = ; } if (tx.status === EthTxStatus.Confirmed) { content = ; } if (tx.status === EthTxStatus.Error) { content = ; } return { id: `eth-${tx.id}`, intent: intentMap[tx.status], onClose: onClose(tx), loader: [EthTxStatus.Pending, EthTxStatus.Complete].includes(tx.status), content, closeAfter, }; }, [onClose] ); useEthTransactionStore.subscribe( (state) => compact(state.transactions.filter((tx) => tx?.dialogOpen)), (txs) => { txs.forEach((tx) => { setToast(fromEthTransaction(tx)); }); } ); };