Skip to content

Commit

Permalink
feat(bridge-ui): handle transaction timeouts (#16912)
Browse files Browse the repository at this point in the history
  • Loading branch information
KorbinianK committed Apr 29, 2024
1 parent 83cdbe8 commit 759c511
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 51 deletions.
32 changes: 0 additions & 32 deletions packages/bridge-ui/src/abi/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3094,16 +3094,6 @@ export const erc20Abi = [
outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }],
stateMutability: 'view',
},
{
type: 'function',
inputs: [
{ name: 'account', internalType: 'address', type: 'address' },
{ name: 'snapshotId', internalType: 'uint256', type: 'uint256' },
],
name: 'balanceOfAt',
outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }],
stateMutability: 'view',
},
{
type: 'function',
inputs: [{ name: '_amount', internalType: 'uint256', type: 'uint256' }],
Expand Down Expand Up @@ -3397,13 +3387,6 @@ export const erc20Abi = [
outputs: [{ name: '', internalType: 'address payable', type: 'address' }],
stateMutability: 'view',
},
{
type: 'function',
inputs: [],
name: 'snapshooter',
outputs: [{ name: '', internalType: 'address', type: 'address' }],
stateMutability: 'view',
},
{
type: 'function',
inputs: [],
Expand Down Expand Up @@ -3432,13 +3415,6 @@ export const erc20Abi = [
outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }],
stateMutability: 'view',
},
{
type: 'function',
inputs: [{ name: 'snapshotId', internalType: 'uint256', type: 'uint256' }],
name: 'totalSupplyAt',
outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }],
stateMutability: 'view',
},
{
type: 'function',
inputs: [
Expand Down Expand Up @@ -3699,14 +3675,6 @@ export const erc20Abi = [
],
name: 'Paused',
},
{
type: 'event',
anonymous: false,
inputs: [
{ name: 'id', internalType: 'uint256', type: 'uint256', indexed: false },
],
name: 'Snapshot',
},
{
type: 'event',
anonymous: false,
Expand Down
2 changes: 1 addition & 1 deletion packages/bridge-ui/src/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const processingFeeComponent = {
};

export const pendingTransaction = {
waitTimeout: 300_000,
waitTimeout: 30_000,
};

export const storageService = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { onMount } from 'svelte';
import { t } from 'svelte-i18n';
import type { Hash } from 'viem';
import type { Hash, Hex } from 'viem';
import { routingContractsMap } from '$bridgeConfig';
import { chainConfig } from '$chainConfig';
Expand All @@ -19,15 +19,15 @@
import { BridgingStatus } from '$components/Bridge/types';
import { Icon, type IconType } from '$components/Icon';
import { successToast } from '$components/NotificationToast';
import { infoToast } from '$components/NotificationToast/NotificationToast.svelte';
import { infoToast, warningToast } from '$components/NotificationToast/NotificationToast.svelte';
import Spinner from '$components/Spinner/Spinner.svelte';
import { type ApproveArgs, bridges, type BridgeTransaction, MessageStatus, type NFTApproveArgs } from '$libs/bridge';
import type { ERC20Bridge } from '$libs/bridge/ERC20Bridge';
import type { ERC721Bridge } from '$libs/bridge/ERC721Bridge';
import type { ERC1155Bridge } from '$libs/bridge/ERC1155Bridge';
import { getBridgeArgs } from '$libs/bridge/getBridgeArgs';
import { handleBridgeError } from '$libs/bridge/handleBridgeErrors';
import { BridgePausedError } from '$libs/error';
import { BridgePausedError, TransactionTimeoutError } from '$libs/error';
import { bridgeTxService } from '$libs/storage';
import { TokenType } from '$libs/token';
import { getTokenApprovalStatus } from '$libs/token/getTokenApprovalStatus';
Expand All @@ -48,6 +48,8 @@
let approving: boolean;
let checking: boolean;
let icon: IconType;
$: statusTitle = '';
$: statusDescription = '';
Expand All @@ -56,17 +58,34 @@
const destinationChain = $destNetwork?.id;
const userAccount = $account?.address;
if (!currentChain || !destinationChain || !userAccount) return; //TODO error handling
if (!currentChain || !destinationChain || !userAccount || !$selectedToken) return; //TODO error handling
const explorer = chainConfig[currentChain]?.blockExplorers?.default.url;
await pendingTransactions.add(txHash, currentChain);
bridgingStatus = BridgingStatus.DONE;
statusTitle = $t('bridge.actions.bridge.success.title');
statusDescription = $t('bridge.step.confirm.bridge.success.message', {
values: { url: `${explorer}/tx/${txHash}` },
});
try {
await pendingTransactions.add(txHash, currentChain);
successToast({
title: $t('bridge.actions.approve.success.title'),
message: $t('bridge.actions.approve.success.message', {
values: {
token: $selectedToken.symbol,
},
}),
});
icon = successIcon;
bridgingStatus = BridgingStatus.DONE;
statusTitle = $t('bridge.actions.bridge.success.title');
statusDescription = $t('bridge.step.confirm.bridge.success.message', {
values: { url: `${explorer}/tx/${txHash}` },
});
} catch (error) {
if (error instanceof TransactionTimeoutError) {
handleTimeout(txHash);
} else {
handleBridgeError(error as Error);
}
}
const bridgeTx = {
hash: txHash,
Expand All @@ -85,6 +104,27 @@
bridgeTxService.addTxByAddress(userAccount, bridgeTx);
};
const handleTimeout = (txHash: Hex) => {
const currentChain = $connectedSourceChain?.id;
const explorer = chainConfig[currentChain]?.blockExplorers?.default.url;
warningToast({
title: $t('bridge.actions.bridge.timeout.title'),
message: $t('bridge.actions.bridge.timeout.message', {
values: {
url: `${explorer}/tx/${approveTxHash}`,
},
}),
});
icon = timeoutIcon;
iconFill = 'fill-warning-sentiment';
bridgingStatus = BridgingStatus.DONE;
statusTitle = $t('bridge.actions.bridge.timeout.title');
statusDescription = $t('bridge.step.confirm.bridge.timeout.message', {
values: { url: `${explorer}/tx/${txHash}` },
});
};
const handleApproveTxHash = async (txHash: Hash) => {
const currentChain = $connectedSourceChain?.id;
Expand Down Expand Up @@ -197,9 +237,11 @@
handleBridgeError(err as Error);
}
}
$: iconFill = '';
$: approveIcon = `approve-${$theme}` as IconType;
$: bridgeIcon = `bridge-${$theme}` as IconType;
$: successIcon = `success-${$theme}` as IconType;
$: timeoutIcon = `exclamation-circle` as IconType;
onMount(() => (bridgingStatus = BridgingStatus.PENDING));
</script>
Expand All @@ -208,7 +250,7 @@
<section id="txStatus">
<div class="flex flex-col justify-content-center items-center">
{#if bridgingStatus === BridgingStatus.DONE}
<Icon type={successIcon} size={160} />
<Icon type={icon} size={160} fillClass={iconFill} />
<div id="text" class="f-col my-[30px] text-center">
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
<h1>{@html statusTitle}</h1>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// Defaults when no value was provided for closeManually
success: false,
error: true,
warning: false,
warning: true,
info: false,
unknown: false,
};
Expand Down
7 changes: 7 additions & 0 deletions packages/bridge-ui/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
"message": "Your funds are being prepared for claiming on Taiko.",
"title": "Transaction completed"
},
"timeout": {
"message": "It took longer than expected to process your transaction, check your wallet for the status.",
"title": "Timed out"
},
"tx": {
"message": "Your bridge transaction was confirmed. The transaction can take a few minutes to complete, track it <a href=\"{url}\" target=\"_blank\" rel=\"noopener noreferrer\"><b>here</b></a>.",
"title": "Transaction sent"
Expand Down Expand Up @@ -154,6 +158,9 @@
"bridge": {
"success": {
"message": "Your bridge transaction was confirmed. The transaction can take a few minutes to complete, track it <a class='link' href=\"{url}\" target=\"_blank\" rel=\"noopener noreferrer\"><b>here</b></a>."
},
"timeout": {
"message": "The network experiences heavy load and it took longer than expected for your transaction to process. You can track the status on the <a class=\"link\" href=\\\"{url}\\\" target=\\\"_blank\\\" rel=\\\"noopener noreferrer\\\"><b>explorer</b></a> and in your wallet."
}
},
"button": {
Expand Down
5 changes: 2 additions & 3 deletions packages/bridge-ui/src/libs/error/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ export class IpfsError extends Error {
export class ClientError extends Error {
name = 'ClientError';
}

export class NoDelaysForBridgeError extends Error {
name = 'NoDelaysForBridgeError';
export class TransactionTimeoutError extends Error {
name = 'TransactionTimeoutError';
}
7 changes: 5 additions & 2 deletions packages/bridge-ui/src/stores/pendingTransactions.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { waitForTransactionReceipt } from '@wagmi/core';
import { writable } from 'svelte/store';
import type { Hex, TransactionReceipt } from 'viem';
import { type Hex, type TransactionReceipt, WaitForTransactionReceiptTimeoutError } from 'viem';

import { pendingTransaction } from '$config';
import { FailedTransactionError } from '$libs/error';
import { FailedTransactionError, TransactionTimeoutError } from '$libs/error';
import { refreshUserBalance } from '$libs/util/balance';
import { Deferred } from '$libs/util/Deferred';
import { getLogger } from '$libs/util/logger';
Expand Down Expand Up @@ -67,6 +67,9 @@ export const pendingTransactions = {
})
.catch((err) => {
console.error(err);
if (err instanceof WaitForTransactionReceiptTimeoutError) {
deferred.reject(new TransactionTimeoutError(`transaction with hash "${hash}" timed out`, { cause: err }));
}
deferred.reject(new FailedTransactionError(`transaction with hash "${hash}" failed`, { cause: err }));
})
.finally(() => {
Expand Down

0 comments on commit 759c511

Please sign in to comment.