import { createContext, useCallback, useContext, useMemo } from 'react'; import { useIsMounted } from 'usehooks-ts'; import type { APIError } from '@/api/types'; import { useLifecycleStatus } from '@/internal/hooks/useLifecycleStatus'; import { GENERIC_ERROR_MESSAGE } from '@/transaction/constants'; import { isUserRejectedRequestError } from '@/transaction/utils/isUserRejectedRequestError'; import { useSignData } from '../hooks/useSignData'; import { type LifecycleStatus, type MessageData } from '../types'; export type SignatureContextType = { lifecycleStatus: LifecycleStatus; handleSign: () => Promise; }; const SignatureContext = createContext(null); export function useSignatureContext() { const context = useContext(SignatureContext); if (!context) { throw new Error( 'useSignatureContext must be used within a SignatureProvider', ); } return context; } export type SignatureProviderProps = { children: React.ReactNode; } & { onSuccess?: (signature: string) => void; onError?: (error: APIError) => void; onStatus?: (status: LifecycleStatus) => void; resetAfter?: number; } & MessageData; export function SignatureProvider({ children, onSuccess, onError, onStatus, domain, types, message, primaryType, resetAfter, }: SignatureProviderProps) { const isMounted = useIsMounted(); const { signData, resetSignData } = useSignData(); const [lifecycleStatus, updateLifecycleStatus] = useLifecycleStatus( { statusName: 'init', statusData: null, }, { onStatus: (status) => { onStatus?.(status); if (status.statusName === 'error') { onError?.(status.statusData); } if (status.statusName !== 'success') { return; } onSuccess?.(status.statusData.signature); if (resetAfter) { setTimeout(() => { if (!isMounted()) return; resetSignData(); updateLifecycleStatus({ statusName: 'init', statusData: null, }); }, resetAfter); } }, }, ); const handleSign = useCallback(async () => { updateLifecycleStatus({ statusName: 'pending', }); try { const signature = await signData({ domain, types, message, primaryType, }); updateLifecycleStatus({ statusName: 'success', statusData: { signature, }, }); } catch (err) { const errorMessage = isUserRejectedRequestError(err) ? 'Request denied.' : GENERIC_ERROR_MESSAGE; updateLifecycleStatus({ statusName: 'error', statusData: { code: 'SmSPc01', // Signature module SignatureProvider component 01 error error: JSON.stringify(err), message: errorMessage, }, }); } }, [domain, types, message, primaryType, updateLifecycleStatus, signData]); const contextValue = useMemo( () => ({ lifecycleStatus, handleSign, }), [lifecycleStatus, handleSign], ); return ( {children} ); }