diff --git a/src/transaction/components/TransactionProvider.test.tsx b/src/transaction/components/TransactionProvider.test.tsx index 7e0fc9e370..3c7cb18d60 100644 --- a/src/transaction/components/TransactionProvider.test.tsx +++ b/src/transaction/components/TransactionProvider.test.tsx @@ -5,6 +5,7 @@ import { useSwitchChain, useWaitForTransactionReceipt, } from 'wagmi'; +import { METHOD_NOT_SUPPORTED_ERROR_SUBSTRING } from '../constants'; import { useCallsStatus } from '../hooks/useCallsStatus'; import { useWriteContract } from '../hooks/useWriteContract'; import { useWriteContracts } from '../hooks/useWriteContracts'; @@ -235,16 +236,13 @@ describe('TransactionProvider', () => { statusWriteContracts: 'IDLE', writeContractsAsync: writeContractsAsyncMock, }); - render( , ); - const button = screen.getByText('Submit'); fireEvent.click(button); - await waitFor(() => { const errorMessage = screen.getByTestId('context-value-errorMessage'); expect(errorMessage.textContent).toBe('Request denied.'); @@ -259,7 +257,6 @@ describe('TransactionProvider', () => { (useCallsStatus as ReturnType).mockReturnValue({ transactionHash: 'hash', }); - render( { , ); - await waitFor(() => { expect(onSuccessMock).toHaveBeenCalledWith({ transactionReceipts: [{ status: 'success' }], @@ -283,21 +279,43 @@ describe('TransactionProvider', () => { switchChainAsync: switchChainAsyncMock, }); (useAccount as ReturnType).mockReturnValue({ chainId: 1 }); - render( , ); - const button = screen.getByText('Submit'); fireEvent.click(button); - await waitFor(() => { expect(switchChainAsyncMock).toHaveBeenCalledWith({ chainId: 2 }); }); }); + it('should call fallbackToWriteContract when executeContracts fails', async () => { + const writeContractsAsyncMock = vi + .fn() + .mockRejectedValue(new Error(METHOD_NOT_SUPPORTED_ERROR_SUBSTRING)); + const writeContractAsyncMock = vi.fn(); + (useWriteContracts as ReturnType).mockReturnValue({ + statusWriteContracts: 'IDLE', + writeContractsAsync: writeContractsAsyncMock, + }); + (useWriteContract as ReturnType).mockReturnValue({ + status: 'IDLE', + writeContractAsync: writeContractAsyncMock, + }); + render( + + + , + ); + const button = screen.getByText('Submit'); + fireEvent.click(button); + await waitFor(() => { + expect(writeContractAsyncMock).toHaveBeenCalled(); + }); + }); + it('should handle generic error during fallback', async () => { const writeContractsAsyncMock = vi .fn() @@ -313,16 +331,13 @@ describe('TransactionProvider', () => { status: 'IDLE', writeContractAsync: writeContractAsyncMock, }); - render( , ); - const button = screen.getByText('Submit'); fireEvent.click(button); - await waitFor(() => { expect(screen.getByTestId('context-value-errorMessage').textContent).toBe( 'Something went wrong. Please try again.', diff --git a/src/transaction/components/TransactionProvider.tsx b/src/transaction/components/TransactionProvider.tsx index 086b1fd6e0..b487ae0eb7 100644 --- a/src/transaction/components/TransactionProvider.tsx +++ b/src/transaction/components/TransactionProvider.tsx @@ -118,8 +118,14 @@ export function TransactionProvider({ }); receipts.push(txnReceipt); } catch (err) { - console.error('getTransactionReceiptsError', err); - setErrorMessage(GENERIC_ERROR_MESSAGE); + setLifeCycleStatus({ + statusName: 'error', + statusData: { + code: 'TmTPc01', // Transaction module TransactionProvider component 01 error + error: JSON.stringify(err), + message: GENERIC_ERROR_MESSAGE, + }, + }); } } setReceiptArray(receipts); @@ -144,7 +150,14 @@ export function TransactionProvider({ const errorMessage = isUserRejectedRequestError(err) ? 'Request denied.' : GENERIC_ERROR_MESSAGE; - setErrorMessage(errorMessage); + setLifeCycleStatus({ + statusName: 'error', + statusData: { + code: 'TmTPc02', // Transaction module TransactionProvider component 02 error + error: JSON.stringify(err), + message: errorMessage, + }, + }); } } }, [contracts, writeContractAsync]); @@ -165,30 +178,6 @@ export function TransactionProvider({ }); }, [writeContractsAsync, contracts, capabilities]); - const handleSubmitErrors = useCallback( - async (err: unknown) => { - // handles EOA writeContracts error - // (fallback to writeContract) - if ( - err instanceof Error && - err.message.includes(METHOD_NOT_SUPPORTED_ERROR_SUBSTRING) - ) { - try { - await fallbackToWriteContract(); - } catch (_err) { - setErrorMessage(GENERIC_ERROR_MESSAGE); - } - // handles user rejected request error - } else if (isUserRejectedRequestError(err)) { - setErrorMessage('Request denied.'); - // handles generic error - } else { - setErrorMessage(GENERIC_ERROR_MESSAGE); - } - }, - [fallbackToWriteContract], - ); - const handleSubmit = useCallback(async () => { setErrorMessage(''); setIsToastVisible(true); @@ -196,9 +185,27 @@ export function TransactionProvider({ await switchChain(chainId); await executeContracts(); } catch (err) { - await handleSubmitErrors(err); + // handles EOA writeContracts error (fallback to writeContract) + if ( + err instanceof Error && + err.message.includes(METHOD_NOT_SUPPORTED_ERROR_SUBSTRING) + ) { + await fallbackToWriteContract(); + return; + } + const errorMessage = isUserRejectedRequestError(err) + ? 'Request denied.' + : GENERIC_ERROR_MESSAGE; + setLifeCycleStatus({ + statusName: 'error', + statusData: { + code: 'TmTPc03', // Transaction module TransactionProvider component 03 error + error: JSON.stringify(err), + message: errorMessage, + }, + }); } - }, [chainId, executeContracts, handleSubmitErrors, switchChain]); + }, [chainId, executeContracts, fallbackToWriteContract, switchChain]); useEffect(() => { if (receiptArray?.length) { diff --git a/src/transaction/hooks/useTransactionReceipts.ts b/src/transaction/hooks/useTransactionReceipts.ts new file mode 100644 index 0000000000..e69de29bb2