(
+const TestComponent = () => {
+ const context = useEarnContext();
+ const handleStatusError = async () => {
+ context.updateLifecycleStatus({
+ statusName: 'error',
+ statusData: {
+ code: 'code',
+ error: 'error_long_messages',
+ message: 'error_long_messages',
+ },
+ });
+ };
+ const handleStatusSuccess = async () => {
+ context.updateLifecycleStatus({
+ statusName: 'success',
+ statusData: {
+ transactionReceipts: [
+ { hash: '0x1235' } as unknown as TransactionReceipt,
+ ],
+ },
+ });
+ };
+
+ return (
+
+
+ {context.lifecycleStatus.statusName}
+
+
+
+
+ );
+};
+
+const wrapper = ({
+ children,
+}: {
+ children?: React.ReactNode;
+}) => (
{children}
@@ -58,6 +101,54 @@ describe('EarnProvider', () => {
});
});
+ it('should emit onError when setLifecycleStatus is called with error', async () => {
+ const onErrorMock = vi.fn();
+ const onStatusMock = vi.fn();
+ (useMorphoVault as Mock).mockReturnValue({
+ asset: DUMMY_ADDRESS,
+ assetDecimals: 18,
+ assetSymbol: 'TEST',
+ balance: '100',
+ totalApy: '0.05',
+ });
+
+ render(
+
+
+ ,
+ );
+
+ const button = screen.getByText('setLifecycleStatus.error');
+ fireEvent.click(button);
+ expect(onErrorMock).toHaveBeenCalled();
+ expect(onStatusMock).toHaveBeenCalled();
+ });
+
+ it('should emit onSuccess when setLifecycleStatus is called with success', async () => {
+ const onSuccessMock = vi.fn();
+ (useMorphoVault as Mock).mockReturnValue({
+ asset: DUMMY_ADDRESS,
+ assetDecimals: 18,
+ assetSymbol: 'TEST',
+ balance: '100',
+ totalApy: '0.05',
+ });
+
+ render(
+
+
+ ,
+ );
+
+ const button = screen.getByText('setLifecycleStatus.success');
+ fireEvent.click(button);
+ expect(onSuccessMock).toHaveBeenCalledWith({ hash: '0x1235' });
+ });
+
it('throws an error when vaultAddress is not provided', () => {
expect(() =>
renderHook(() => useEarnContext(), {
diff --git a/src/earn/components/EarnProvider.tsx b/src/earn/components/EarnProvider.tsx
index 00d76c7869..c144345450 100644
--- a/src/earn/components/EarnProvider.tsx
+++ b/src/earn/components/EarnProvider.tsx
@@ -7,6 +7,7 @@ import {
createContext,
useCallback,
useContext,
+ useEffect,
useMemo,
useState,
} from 'react';
@@ -25,6 +26,9 @@ export function EarnProvider({
vaultAddress,
children,
isSponsored,
+ onError,
+ onStatus,
+ onSuccess,
}: EarnProviderReact) {
if (!vaultAddress) {
throw new Error(
@@ -43,6 +47,16 @@ export function EarnProvider({
const [withdrawAmount, setWithdrawAmount] = useState('');
const [depositAmount, setDepositAmount] = useState('');
+ useEffect(() => {
+ if (lifecycleStatus.statusName === 'error') {
+ onError?.(lifecycleStatus.statusData);
+ }
+ if (lifecycleStatus?.statusName === 'success') {
+ onSuccess?.(lifecycleStatus?.statusData?.transactionReceipts?.[0]);
+ }
+ onStatus?.(lifecycleStatus);
+ }, [lifecycleStatus, onStatus, onError, onSuccess]);
+
const {
asset,
balance: depositedBalance,
diff --git a/src/earn/types.ts b/src/earn/types.ts
index a7d224840d..02a4f37f84 100644
--- a/src/earn/types.ts
+++ b/src/earn/types.ts
@@ -1,10 +1,11 @@
+import type { TransactionError } from '@/api/types';
import type { UseMorphoVaultReturnType } from '@/earn/hooks/useMorphoVault';
import type { LifecycleStatusUpdate } from '@/internal/types';
import type { Token } from '@/token';
import type { Call } from '@/transaction/types';
import type { LifecycleStatus as TransactionLifecycleStatus } from '@/transaction/types';
import type React from 'react';
-import type { Address } from 'viem';
+import type { Address, TransactionReceipt } from 'viem';
/**
* Note: exported as public Type
@@ -14,6 +15,12 @@ export type EarnReact = {
className?: string;
vaultAddress: Address;
isSponsored?: boolean;
+ /** An optional callback function that handles errors within the provider. */
+ onError?: (error: TransactionError) => void;
+ /** An optional callback function that exposes the component lifecycle state */
+ onStatus?: (lifecycleStatus: LifecycleStatus) => void;
+ /** An optional callback function that exposes the transaction receipt */
+ onSuccess?: (transactionReceipt?: TransactionReceipt) => void;
};
/**
@@ -23,6 +30,12 @@ export type EarnProviderReact = {
children: React.ReactNode;
vaultAddress: Address;
isSponsored?: boolean;
+ /** An optional callback function that handles errors within the provider. */
+ onError?: (error: TransactionError) => void;
+ /** An optional callback function that exposes the component lifecycle state */
+ onStatus?: (lifecycleStatus: LifecycleStatus) => void;
+ /** An optional callback function that exposes the transaction receipt */
+ onSuccess?: (transactionReceipt?: TransactionReceipt) => void;
};
/**