feat: add seperate transaction toast for completed eth transaction (#2733)

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
This commit is contained in:
Bartłomiej Głownia 2023-01-25 21:25:44 +01:00 committed by GitHub
parent 3fadda15fd
commit 129274a8e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 81 additions and 71 deletions

View File

@ -1,3 +1,4 @@
import type { ReactNode } from 'react';
import { useAssetsDataProvider } from '@vegaprotocol/assets'; import { useAssetsDataProvider } from '@vegaprotocol/assets';
import { ETHERSCAN_TX, useEtherscanLink } from '@vegaprotocol/environment'; import { ETHERSCAN_TX, useEtherscanLink } from '@vegaprotocol/environment';
import { formatNumber, t, toBigNum } from '@vegaprotocol/react-helpers'; import { formatNumber, t, toBigNum } from '@vegaprotocol/react-helpers';
@ -18,60 +19,59 @@ const intentMap: { [s in EthTxStatus]: Intent } = {
Requested: Intent.Warning, Requested: Intent.Warning,
Pending: Intent.Warning, Pending: Intent.Warning,
Error: Intent.Danger, Error: Intent.Danger,
Complete: Intent.Success, Complete: Intent.Warning,
Confirmed: Intent.Success, Confirmed: Intent.Success,
}; };
const isWithdrawTransaction = (tx: EthStoredTxState) =>
tx.methodName === 'withdraw_asset';
const isDepositTransaction = (tx: EthStoredTxState) =>
tx.methodName === 'withdraw_asset';
const EthTransactionDetails = ({ tx }: { tx: EthStoredTxState }) => { const EthTransactionDetails = ({ tx }: { tx: EthStoredTxState }) => {
const { data } = useAssetsDataProvider(); const { data: assets } = useAssetsDataProvider();
if (!data) return null; if (!assets) return null;
const ETH_WITHDRAW = const isWithdraw = isWithdrawTransaction(tx);
tx.methodName === 'withdraw_asset' && tx.args.length > 2 && tx.assetId; const isDeposit = isDepositTransaction(tx);
const ETH_DEPOSIT =
tx.methodName === 'deposit_asset' && tx.args.length > 2 && tx.assetId;
let label = '';
if (ETH_WITHDRAW) label = t('Withdraw');
if (ETH_DEPOSIT) label = t('Deposit');
if (ETH_WITHDRAW || ETH_DEPOSIT) {
const asset = data.find((a) => a.id === tx.assetId);
let assetInfo: ReactNode;
if ((isWithdraw || isDeposit) && tx.args.length > 2 && tx.assetId) {
const asset = assets.find((a) => a.id === tx.assetId);
if (asset) { if (asset) {
const num = formatNumber( let label = '';
toBigNum(tx.args[1], asset.decimals), if (isWithdraw) label = t('Withdraw');
asset.decimals if (isDeposit) label = t('Deposit');
); assetInfo = (
const details = (
<div className="mt-[5px]"> <div className="mt-[5px]">
<span className="font-mono text-xs p-1 bg-gray-100 rounded"> <span className="font-mono text-xs p-1 bg-gray-100 rounded">
{label} {num} {asset.symbol} {label}{' '}
{formatNumber(toBigNum(tx.args[1], asset.decimals), asset.decimals)}{' '}
{asset.symbol}
</span> </span>
</div> </div>
); );
return (
<>
{details}
{tx.requiresConfirmation &&
[EthTxStatus.Pending].includes(tx.status) && (
<div className="mt-[10px]">
<span className="font-mono text-xs">
{t('Awaiting confirmations')}{' '}
{`(${tx.confirmations}/${tx.requiredConfirmations})`}
</span>
<ProgressBar
value={(tx.confirmations / tx.requiredConfirmations) * 100}
intent={Intent.Warning}
/>
</div>
)}
</>
);
} }
} }
return null; return (
<>
{assetInfo}
{tx.status === EthTxStatus.Pending && (
<div className="mt-[10px]">
<span className="font-mono text-xs">
{t('Awaiting confirmations')}{' '}
{`(${tx.confirmations}/${tx.requiredConfirmations})`}
</span>
<ProgressBar
value={(tx.confirmations / tx.requiredConfirmations) * 100}
intent={Intent.Warning}
/>
</div>
)}
</>
);
}; };
type EthTxToastContentProps = { type EthTxToastContentProps = {
@ -93,21 +93,11 @@ const EthTxRequestedToastContent = ({ tx }: EthTxToastContentProps) => {
}; };
const EthTxPendingToastContent = ({ tx }: EthTxToastContentProps) => { const EthTxPendingToastContent = ({ tx }: EthTxToastContentProps) => {
const etherscanLink = useEtherscanLink();
return ( return (
<div> <div>
<h3 className="font-bold">{t('Awaiting confirmation')}</h3> <h3 className="font-bold">{t('Awaiting confirmation')}</h3>
<p>{t('Please wait for your transaction to be confirmed')}</p> <p>{t('Please wait for your transaction to be confirmed')}</p>
{tx.txHash && ( <EtherscanLink tx={tx} />
<p className="break-all">
<ExternalLink
href={etherscanLink(ETHERSCAN_TX.replace(':hash', tx.txHash))}
rel="noreferrer"
>
{t('View on Etherscan')}
</ExternalLink>
</p>
)}
<EthTransactionDetails tx={tx} /> <EthTransactionDetails tx={tx} />
</div> </div>
); );
@ -130,22 +120,43 @@ const EthTxErrorToastContent = ({ tx }: EthTxToastContentProps) => {
); );
}; };
const EthTxConfirmedToastContent = ({ tx }: EthTxToastContentProps) => { const EtherscanLink = ({ tx }: EthTxToastContentProps) => {
const etherscanLink = useEtherscanLink(); const etherscanLink = useEtherscanLink();
return tx.txHash ? (
<p className="break-all">
<ExternalLink
href={etherscanLink(ETHERSCAN_TX.replace(':hash', tx.txHash))}
rel="noreferrer"
>
{t('View on Etherscan')}
</ExternalLink>
</p>
) : null;
};
const EthTxConfirmedToastContent = ({ tx }: EthTxToastContentProps) => {
return ( return (
<div> <div>
<h3 className="font-bold">{t('Transaction completed')}</h3> <h3 className="font-bold">{t('Transaction confirmed')}</h3>
<p>{t('Your transaction has been completed')}</p> <p>{t('Your transaction has been confirmed')}</p>
{tx.txHash && ( <EtherscanLink tx={tx} />
<p className="break-all"> <EthTransactionDetails tx={tx} />
<ExternalLink </div>
href={etherscanLink(ETHERSCAN_TX.replace(':hash', tx.txHash))} );
rel="noreferrer" };
>
{t('View on Etherscan')} const EthTxCompletedToastContent = ({ tx }: EthTxToastContentProps) => {
</ExternalLink> const isDeposit = isDepositTransaction(tx);
</p> return (
)} <div>
<h3 className="font-bold">
{t('Processing')} {isDeposit && 'deposit'}
</h3>
<p>
{t('Your transaction has been completed.')}
{isDeposit && t('Waiting for deposit confirmation')}
</p>
<EtherscanLink tx={tx} />
<EthTransactionDetails tx={tx} /> <EthTransactionDetails tx={tx} />
</div> </div>
); );
@ -167,10 +178,10 @@ export const useEthereumTransactionToasts = () => {
if (tx.status === EthTxStatus.Pending) { if (tx.status === EthTxStatus.Pending) {
content = <EthTxPendingToastContent tx={tx} />; content = <EthTxPendingToastContent tx={tx} />;
} }
if ( if (tx.status === EthTxStatus.Complete) {
tx.status === EthTxStatus.Confirmed || content = <EthTxCompletedToastContent tx={tx} />;
tx.status === EthTxStatus.Complete }
) { if (tx.status === EthTxStatus.Confirmed) {
content = <EthTxConfirmedToastContent tx={tx} />; content = <EthTxConfirmedToastContent tx={tx} />;
} }
if (tx.status === EthTxStatus.Error) { if (tx.status === EthTxStatus.Error) {
@ -181,7 +192,7 @@ export const useEthereumTransactionToasts = () => {
id: `eth-${tx.id}`, id: `eth-${tx.id}`,
intent: intentMap[tx.status], intent: intentMap[tx.status],
onClose: () => dismissEthTransaction(tx.id), onClose: () => dismissEthTransaction(tx.id),
loader: tx.status === EthTxStatus.Pending, loader: [EthTxStatus.Pending, EthTxStatus.Complete].includes(tx.status),
content, content,
}; };
}, },

View File

@ -115,6 +115,7 @@ export const Toast = ({
> >
<div className="flex relative"> <div className="flex relative">
<button <button
type="button"
data-testid="toast-close" data-testid="toast-close"
onClick={closeToast} onClick={closeToast}
className="absolute p-2 top-0 right-0" className="absolute p-2 top-0 right-0"

View File

@ -120,9 +120,7 @@ export const useEthTransactionStore = create<EthTransactionStore>(
produce((state: EthTransactionStore) => { produce((state: EthTransactionStore) => {
const transaction = state.transactions.find( const transaction = state.transactions.find(
(transaction) => (transaction) =>
transaction && transaction && deposit.txHash === transaction.txHash
transaction.status === EthTxStatus.Pending &&
deposit.txHash === transaction.txHash
); );
if (!transaction) { if (!transaction) {
return; return;