Skip to content

Commit 4820589

Browse files
committed
fix: ask for fee upfront in useRevokeBatchQueuedTransactions
1 parent 14fd5d7 commit 4820589

File tree

9 files changed

+71
-12
lines changed

9 files changed

+71
-12
lines changed

lib/hooks/ethereum/useFeePayment.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,21 @@
22

33
import { isNonZeroFeeDollarAmount } from 'components/allowances/controls/batch-revoke/fee';
44
import { DONATION_ADDRESS } from 'lib/constants';
5-
import { type TransactionSubmitted, TransactionType } from 'lib/interfaces';
5+
import type { TransactionSubmitted } from 'lib/interfaces';
66
import { waitForTransactionConfirmation } from 'lib/utils';
77
import analytics from 'lib/utils/analytics';
88
import { type DocumentedChainId, getChainNativeToken, isTestnetChain } from 'lib/utils/chains';
9+
import { isNoFeeRequiredError } from 'lib/utils/errors';
910
import { HOUR } from 'lib/utils/time';
1011
import useLocalStorage from 'use-local-storage';
1112
import { parseEther, type SendTransactionParameters } from 'viem';
1213
import { usePublicClient, useWalletClient } from 'wagmi';
13-
import { useHandleTransaction } from './useHandleTransaction';
1414
import { useNativeTokenPrice } from './useNativeTokenPrice';
1515

1616
export const useFeePayment = (chainId: number) => {
1717
const nativeToken = getChainNativeToken(chainId)!;
1818
const { data: walletClient } = useWalletClient({ chainId });
1919
const publicClient = usePublicClient({ chainId })!;
20-
const handleTransaction = useHandleTransaction(chainId);
2120
const { nativeTokenPrice } = useNativeTokenPrice(chainId);
2221

2322
const [lastFeePayments, setLastFeePayments] = useLocalStorage<Record<string, number>>('last-fee-payments', {});
@@ -31,7 +30,7 @@ export const useFeePayment = (chainId: number) => {
3130
const lastFeePayment = lastFeePayments[feePaymentKey];
3231

3332
if (lastFeePayment && Date.now() - lastFeePayment < 1 * HOUR) {
34-
throw new Error('User rejected fee payment: Fee payment already registered in the last hour');
33+
throw new Error('No fee required: Fee payment already registered in the last hour');
3534
}
3635

3736
const hash = await walletClient.sendTransaction(await prepareFeePayment(dollarAmount));
@@ -47,11 +46,11 @@ export const useFeePayment = (chainId: number) => {
4746
}
4847

4948
if (!nativeTokenPrice) {
50-
throw new Error('User rejected fee payment: Could not get native token price for fee payment');
49+
throw new Error('No fee required: Could not get native token price for fee payment');
5150
}
5251

5352
if (!isNonZeroFeeDollarAmount(dollarAmount)) {
54-
throw new Error('User rejected fee payment');
53+
throw new Error('No fee required: Fee amount is zero');
5554
}
5655

5756
const tokenAmount = Number(dollarAmount) / nativeTokenPrice;
@@ -68,9 +67,14 @@ export const useFeePayment = (chainId: number) => {
6867
const sendFeePayment = async (dollarAmount: string): Promise<TransactionSubmitted | undefined> => {
6968
if (!dollarAmount || Number(dollarAmount) === 0) return;
7069

71-
const transactionSubmitted = await handleTransaction(sendFeePaymentInternal(dollarAmount), TransactionType.FEE);
72-
if (transactionSubmitted) trackFeePaid(chainId, dollarAmount);
73-
return transactionSubmitted;
70+
try {
71+
const transactionSubmitted = await sendFeePaymentInternal(dollarAmount);
72+
trackFeePaid(chainId, dollarAmount);
73+
return transactionSubmitted;
74+
} catch (error) {
75+
if (isNoFeeRequiredError(error)) return;
76+
throw error;
77+
}
7478
};
7579

7680
return { prepareFeePayment, sendFeePayment, nativeToken };

lib/hooks/ethereum/useHandleTransaction.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { displayTransactionSubmittedToast } from 'components/common/TransactionSubmittedToast';
22
import { type TransactionSubmitted, TransactionType } from 'lib/interfaces';
3-
import { isLedgerNanoSError, isRevertedError, isUserRejectionError, parseErrorMessage } from 'lib/utils/errors';
3+
import {
4+
isLedgerNanoSError,
5+
isNoFeeRequiredError,
6+
isRevertedError,
7+
isUserRejectionError,
8+
parseErrorMessage,
9+
} from 'lib/utils/errors';
410
import { useTranslations } from 'next-intl';
511
import { toast } from 'react-toastify';
612
import { stringify } from 'viem';
@@ -14,6 +20,7 @@ export const useHandleTransaction = (chainId: number) => {
1420

1521
// Don't show error toasts for user denied transactions
1622
if (isUserRejectionError(message)) return;
23+
if (isNoFeeRequiredError(message)) return;
1724

1825
console.debug(stringify(e, null, 2));
1926

@@ -44,7 +51,7 @@ export const useHandleTransaction = (chainId: number) => {
4451
try {
4552
const transaction = await transactionPromise;
4653

47-
if (transaction.hash && type !== TransactionType.FEE) {
54+
if (transaction.hash) {
4855
displayTransactionSubmittedToast(chainId, transaction.hash);
4956
}
5057

lib/hooks/ethereum/useRevokeBatchQueuedTransactions.tsx

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,50 @@ import {
1010
trackRevokeTransaction,
1111
} from 'lib/utils/allowances';
1212
import { trackBatchRevoke } from 'lib/utils/batch-revoke';
13+
import { isUserRejectionError, parseErrorMessage } from 'lib/utils/errors';
14+
import { useTranslations } from 'next-intl';
1315
import type PQueue from 'p-queue';
16+
import { toast } from 'react-toastify';
1417
import { useWalletClient } from 'wagmi';
1518
import { useTransactionStore, wrapTransaction } from '../../stores/transaction-store';
1619
import { useAddressPageContext } from '../page-context/AddressPageContext';
1720
import { useFeePayment } from './useFeePayment';
1821

1922
export const useRevokeBatchQueuedTransactions = (allowances: TokenAllowanceData[], onUpdate: OnUpdate) => {
23+
const t = useTranslations();
2024
const { getTransaction, updateTransaction } = useTransactionStore();
2125
const { address, selectedChainId } = useAddressPageContext();
2226
const { sendFeePayment } = useFeePayment(selectedChainId);
2327
const { data: walletClient } = useWalletClient();
2428

2529
const revoke = async (REVOKE_QUEUE: PQueue, feeDollarAmount: string) => {
30+
// Pay the fee before revoking the allowances
31+
if (isNonZeroFeeDollarAmount(feeDollarAmount)) {
32+
allowances.forEach((allowance) => {
33+
updateTransaction(getAllowanceKey(allowance), { status: 'preparing' });
34+
});
35+
36+
try {
37+
await sendFeePayment(feeDollarAmount);
38+
} catch (error) {
39+
if (isUserRejectionError(error)) {
40+
toast.error(t('common.toasts.fee_payment_rejected'));
41+
} else {
42+
toast.error(t('common.toasts.fee_payment_failed', { message: parseErrorMessage(error) }));
43+
}
44+
45+
console.error(error);
46+
47+
allowances.forEach((allowance) => {
48+
updateTransaction(getAllowanceKey(allowance), { status: 'not_started' });
49+
});
50+
51+
return;
52+
}
53+
}
54+
2655
await Promise.race([
2756
Promise.all([
28-
isNonZeroFeeDollarAmount(feeDollarAmount) ? sendFeePayment(feeDollarAmount) : Promise.resolve(),
2957
...allowances.map(async (allowance) => {
3058
// Skip if already confirmed or pending
3159
if (['confirmed', 'pending'].includes(getTransaction(getAllowanceKey(allowance)).status)) return;

lib/utils/errors.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ export const isUserRejectionError = (error?: string | any): boolean => {
1919
return false;
2020
};
2121

22+
export const isNoFeeRequiredError = (error?: string | any): boolean => {
23+
if (!error) return false;
24+
25+
if (typeof error !== 'string') {
26+
return isNoFeeRequiredError(parseErrorMessage(error)) || isNoFeeRequiredError(stringifyError(error));
27+
}
28+
29+
return error?.toLowerCase()?.includes('no fee required');
30+
};
31+
2232
export const isAccountUpgradeRejectionError = (error?: string | any): boolean => {
2333
if (!error) return false;
2434

locales/en/common.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@
143143
"clipboard_failed": "❌ Couldn't copy to clipboard",
144144
"clipboard_success": "✅ Copied to clipboard",
145145
"donation_sent": "💪 Thanks for the donation!",
146+
"fee_payment_failed": "❌ Fee payment failed: {message}",
147+
"fee_payment_rejected": "❌ Please pay the batch revoke fee to continue",
146148
"revoke_failed": "❌ Revoking failed: {message}",
147149
"revoke_failed_ledger_nano_s": "❌ Ledger Nano S was deprecated by Ledger and does not support revoking NFT approvals any more. It is recommended to upgrade to a newer Ledger device.",
148150
"revoke_failed_revert": "❌ The creator of this token has disabled or paused approving and revoking approvals ({message})",

locales/es/common.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@
143143
"clipboard_failed": "❌ No se pudo copiar al portapapeles",
144144
"clipboard_success": "✅ Copiado al portapapeles",
145145
"donation_sent": "💪 ¡Gracias por la donación!",
146+
"fee_payment_failed": "❌ Fee payment failed: {message}",
147+
"fee_payment_rejected": "❌ Please pay the batch revoke fee to continue",
146148
"revoke_failed": "❌ No se pudo revocar la autorización: {message}",
147149
"revoke_failed_ledger_nano_s": "❌ Ledger Nano S fue obsoleto por Ledger y ya no admite la revocación de las aprobaciones de NFT. Se recomienda actualizar a un dispositivo Ledger más nuevo.",
148150
"revoke_failed_revert": "❌ El creador de este token ha deshabilitado o pausado la aprobación y revocación de aprobaciones ({message})",

locales/ja/common.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@
143143
"clipboard_failed": "❌ クリップボードにコピーできませんでした。",
144144
"clipboard_success": "✅ クリップボードにコピーされました",
145145
"donation_sent": "💪 寄付いただき、ありがとうございます!",
146+
"fee_payment_failed": "❌ Fee payment failed: {message}",
147+
"fee_payment_rejected": "❌ Please pay the batch revoke fee to continue",
146148
"revoke_failed": "❌ 許可のリボークに失敗しました: {message}",
147149
"revoke_failed_ledger_nano_s": "❌ Ledger Nano SはLedgerによって非推奨となり、NFT承認の取り消し機能をサポートしなくなりました。新しいLedgerデバイスへのアップグレードが推奨されます。",
148150
"revoke_failed_revert": "❌ このトークンの作成者は、承認と承認取り消しの機能を無効化または一時停止しました({message})",

locales/ru/common.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@
143143
"clipboard_failed": "❌ Не удалось скопировать в буфер",
144144
"clipboard_success": "✅ Скопировано в буфер обмена",
145145
"donation_sent": "💪 Спасибо за пожертвование!",
146+
"fee_payment_failed": "❌ Fee payment failed: {message}",
147+
"fee_payment_rejected": "❌ Please pay the batch revoke fee to continue",
146148
"revoke_failed": "❌ Отменить доступ не удалось: {message}",
147149
"revoke_failed_ledger_nano_s": "❌ Ledger Nano S был снят с поддержки Ledger и больше не поддерживает отзыв NFT подтверждений. Рекомендуется перейти на более новое устройство Ledger.",
148150
"revoke_failed_revert": "❌ Создатель этого токена отключил или приостановил утверждение и отзыв разрешений ({message})",

locales/zh/common.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@
143143
"clipboard_failed": "❌ 无法复制到剪贴板",
144144
"clipboard_success": "✅ 已复制到剪贴板",
145145
"donation_sent": "💪 感谢捐赠!",
146+
"fee_payment_failed": "❌ Fee payment failed: {message}",
147+
"fee_payment_rejected": "❌ Please pay the batch revoke fee to continue",
146148
"revoke_failed": "❌ 撤销授权许可失败:{message}",
147149
"revoke_failed_ledger_nano_s": "❌ Ledger Nano S 已被 Ledger 弃用,不再支持撤销 NFT 授权。建议升级到较新的 Ledger 设备。",
148150
"revoke_failed_revert": "❌该代币创建者已禁用或暂停批准及撤销批准功能({message})",

0 commit comments

Comments
 (0)