* Support CCTP and noble auto deposits on testnet * slippage * fix fees * update isCctp, and comments * bump packages * cctp.json * Bump abacus * fix error * fix loading button
135 lines
4.6 KiB
TypeScript
135 lines
4.6 KiB
TypeScript
import { createContext, useContext, useCallback, useEffect, useMemo } from 'react';
|
|
import { useQuery } from 'react-query';
|
|
|
|
import { LOCAL_STORAGE_VERSIONS, LocalStorageKey } from '@/constants/localStorage';
|
|
import { type TransferNotifcation } from '@/constants/notifications';
|
|
import { useAccounts } from '@/hooks/useAccounts';
|
|
|
|
import { fetchSquidStatus, STATUS_ERROR_GRACE_PERIOD } from '@/lib/squid';
|
|
|
|
import { useLocalStorage } from './useLocalStorage';
|
|
|
|
const LocalNotificationsContext = createContext<
|
|
ReturnType<typeof useLocalNotificationsContext> | undefined
|
|
>(undefined);
|
|
|
|
LocalNotificationsContext.displayName = 'LocalNotifications';
|
|
|
|
export const LocalNotificationsProvider = ({ ...props }) => (
|
|
<LocalNotificationsContext.Provider value={useLocalNotificationsContext()} {...props} />
|
|
);
|
|
|
|
export const useLocalNotifications = () => useContext(LocalNotificationsContext)!;
|
|
|
|
const TRANSFER_STATUS_FETCH_INTERVAL = 10_000;
|
|
const ERROR_COUNT_THRESHOLD = 3;
|
|
|
|
const useLocalNotificationsContext = () => {
|
|
// transfer notifications
|
|
const [allTransferNotifications, setAllTransferNotifications] = useLocalStorage<{
|
|
[key: `dydx${string}`]: TransferNotifcation[];
|
|
version: string;
|
|
}>({
|
|
key: LocalStorageKey.TransferNotifications,
|
|
defaultValue: {
|
|
version: LOCAL_STORAGE_VERSIONS[LocalStorageKey.TransferNotifications],
|
|
},
|
|
});
|
|
|
|
// Ensure version matches, otherwise wipe
|
|
useEffect(() => {
|
|
if (
|
|
allTransferNotifications?.version !==
|
|
LOCAL_STORAGE_VERSIONS[LocalStorageKey.TransferNotifications]
|
|
) {
|
|
setAllTransferNotifications({
|
|
version: LOCAL_STORAGE_VERSIONS[LocalStorageKey.TransferNotifications],
|
|
});
|
|
}
|
|
}, [allTransferNotifications]);
|
|
|
|
const { dydxAddress } = useAccounts();
|
|
|
|
const transferNotifications = dydxAddress ? allTransferNotifications[dydxAddress] || [] : [];
|
|
|
|
const setTransferNotifications = useCallback(
|
|
(notifications: TransferNotifcation[]) => {
|
|
if (!dydxAddress) return;
|
|
const updatedNotifications = { ...allTransferNotifications };
|
|
updatedNotifications[dydxAddress] = notifications;
|
|
setAllTransferNotifications(updatedNotifications);
|
|
},
|
|
[setAllTransferNotifications, dydxAddress]
|
|
);
|
|
|
|
const addTransferNotification = useCallback(
|
|
(notification: TransferNotifcation) =>
|
|
setTransferNotifications([...transferNotifications, notification]),
|
|
[transferNotifications]
|
|
);
|
|
|
|
useQuery({
|
|
queryKey: 'getTransactionStatus',
|
|
queryFn: async () => {
|
|
const processTransferNotifications = async (transferNotifications: TransferNotifcation[]) => {
|
|
const newTransferNotifications = await Promise.all(
|
|
transferNotifications.map(async (transferNotification) => {
|
|
const {
|
|
txHash,
|
|
toChainId,
|
|
fromChainId,
|
|
triggeredAt,
|
|
isCctp,
|
|
errorCount,
|
|
status: currentStatus,
|
|
} = transferNotification;
|
|
|
|
// @ts-ignore status.errors is not in the type definition but can be returned
|
|
// also error can some time come back as an empty object so we need to ignore for that
|
|
const hasErrors = !!currentStatus?.errors ||
|
|
(currentStatus?.error && Object.keys(currentStatus.error).length !== 0);
|
|
|
|
if (
|
|
!hasErrors &&
|
|
(!currentStatus?.squidTransactionStatus ||
|
|
currentStatus?.squidTransactionStatus === 'ongoing')
|
|
) {
|
|
try {
|
|
const status = await fetchSquidStatus({
|
|
transactionId: txHash,
|
|
toChainId,
|
|
fromChainId,
|
|
}, isCctp);
|
|
|
|
if (status) {
|
|
transferNotification.status = status;
|
|
}
|
|
} catch (error) {
|
|
if (!triggeredAt || Date.now() - triggeredAt > STATUS_ERROR_GRACE_PERIOD) {
|
|
if (errorCount && errorCount > ERROR_COUNT_THRESHOLD) {
|
|
transferNotification.status = error;
|
|
} else {
|
|
transferNotification.errorCount = errorCount ? errorCount + 1 : 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return transferNotification;
|
|
})
|
|
);
|
|
|
|
return newTransferNotifications;
|
|
};
|
|
const newTransferNotifications = await processTransferNotifications(transferNotifications);
|
|
setTransferNotifications(newTransferNotifications);
|
|
},
|
|
refetchInterval: TRANSFER_STATUS_FETCH_INTERVAL,
|
|
});
|
|
|
|
return {
|
|
transferNotifications,
|
|
addTransferNotification,
|
|
};
|
|
};
|