UNPKG

@lifi/widget

Version:

LI.FI Widget for cross-chain bridging and swapping. It will drive your multi-chain strategy and attract new users from everywhere.

154 lines 8.85 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import Delete from '@mui/icons-material/Delete'; import { Box, Button, Tooltip } from '@mui/material'; import { useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useLocation } from 'react-router-dom'; import { ContractComponent } from '../../components/ContractComponent/ContractComponent.js'; import { WarningMessages } from '../../components/Messages/WarningMessages.js'; import { PageContainer } from '../../components/PageContainer.js'; import { getStepList } from '../../components/Step/StepList.js'; import { TransactionDetails } from '../../components/TransactionDetails.js'; import { useAddressActivity } from '../../hooks/useAddressActivity.js'; import { useHeader } from '../../hooks/useHeader.js'; import { useNavigateBack } from '../../hooks/useNavigateBack.js'; import { useRouteExecution } from '../../hooks/useRouteExecution.js'; import { useWidgetEvents } from '../../hooks/useWidgetEvents.js'; import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'; import { useFieldActions } from '../../stores/form/useFieldActions.js'; import { RouteExecutionStatus } from '../../stores/routes/types.js'; import { WidgetEvent } from '../../types/events.js'; import { HiddenUI } from '../../types/widget.js'; import { getAccumulatedFeeCostsBreakdown } from '../../utils/fees.js'; import { ConfirmToAddressSheet } from './ConfirmToAddressSheet.js'; import { ExchangeRateBottomSheet } from './ExchangeRateBottomSheet.js'; import { RouteTracker } from './RouteTracker.js'; import { StartTransactionButton } from './StartTransactionButton.js'; import { StatusBottomSheet } from './StatusBottomSheet.js'; import { TokenValueBottomSheet } from './TokenValueBottomSheet.js'; import { calculateValueLossPercentage, getTokenValueLossThreshold, } from './utils.js'; export const TransactionPage = () => { const { t } = useTranslation(); const { setFieldValue } = useFieldActions(); const emitter = useWidgetEvents(); const { navigateBack } = useNavigateBack(); const { subvariant, subvariantOptions, contractSecondaryComponent, hiddenUI, } = useWidgetConfig(); const { state } = useLocation(); const stateRouteId = state?.routeId; const [routeId, setRouteId] = useState(stateRouteId); const [routeRefreshing, setRouteRefreshing] = useState(false); const tokenValueBottomSheetRef = useRef(null); const exchangeRateBottomSheetRef = useRef(null); const confirmToAddressSheetRef = useRef(null); const onAcceptExchangeRateUpdate = (resolver, data) => { exchangeRateBottomSheetRef.current?.open(resolver, data); }; const { route, status, executeRoute, restartRoute, deleteRoute } = useRouteExecution({ routeId: routeId, onAcceptExchangeRateUpdate, }); const { toAddress, hasActivity, isLoading: isLoadingAddressActivity, isFetched: isActivityAddressFetched, } = useAddressActivity(route?.toChainId); const getHeaderTitle = () => { if (subvariant === 'custom') { return t(`header.${subvariantOptions?.custom ?? 'checkout'}`); } if (route) { const transactionType = route.fromChainId === route.toChainId ? 'swap' : 'bridge'; return status === RouteExecutionStatus.Idle ? t(`button.${transactionType}Review`) : t(`header.${transactionType}`); } return t('header.exchange'); }; const headerAction = useMemo(() => status === RouteExecutionStatus.Idle ? (_jsx(RouteTracker, { observableRouteId: stateRouteId, onChange: setRouteId, onFetching: setRouteRefreshing })) : undefined, [stateRouteId, status]); useHeader(getHeaderTitle(), headerAction); // biome-ignore lint/correctness/useExhaustiveDependencies: We want to emit event only when the page is mounted useEffect(() => { if (status === RouteExecutionStatus.Idle) { emitter.emit(WidgetEvent.ReviewTransactionPageEntered, route); } }, []); if (!route) { return null; } const handleExecuteRoute = () => { if (tokenValueBottomSheetRef.current?.isOpen()) { const { gasCostUSD, feeCostUSD } = getAccumulatedFeeCostsBreakdown(route); const fromAmountUSD = Number.parseFloat(route.fromAmountUSD); const toAmountUSD = Number.parseFloat(route.toAmountUSD); emitter.emit(WidgetEvent.RouteHighValueLoss, { fromAmountUSD, toAmountUSD, gasCostUSD, feeCostUSD, valueLoss: calculateValueLossPercentage(fromAmountUSD, toAmountUSD, gasCostUSD, feeCostUSD), }); } tokenValueBottomSheetRef.current?.close(); executeRoute(); setFieldValue('fromAmount', ''); if (subvariant === 'custom') { setFieldValue('fromToken', ''); setFieldValue('toToken', ''); } }; const handleStartClick = async () => { if (status === RouteExecutionStatus.Idle) { if (toAddress && !hasActivity && !isLoadingAddressActivity && isActivityAddressFetched && !hiddenUI?.includes(HiddenUI.LowAddressActivityConfirmation)) { confirmToAddressSheetRef.current?.open(); return; } const { gasCostUSD, feeCostUSD } = getAccumulatedFeeCostsBreakdown(route); const fromAmountUSD = Number.parseFloat(route.fromAmountUSD); const toAmountUSD = Number.parseFloat(route.toAmountUSD); const tokenValueLossThresholdExceeded = getTokenValueLossThreshold(fromAmountUSD, toAmountUSD, gasCostUSD, feeCostUSD); if (tokenValueLossThresholdExceeded && subvariant !== 'custom') { tokenValueBottomSheetRef.current?.open(); } else { handleExecuteRoute(); } } if (status === RouteExecutionStatus.Failed) { restartRoute(); } }; const handleRemoveRoute = () => { navigateBack(); deleteRoute(); }; const getButtonText = () => { switch (status) { case RouteExecutionStatus.Idle: switch (subvariant) { case 'custom': return subvariantOptions?.custom === 'deposit' ? t('button.deposit') : t('button.buy'); case 'refuel': return t('button.startBridging'); default: { const transactionType = route.fromChainId === route.toChainId ? 'Swapping' : 'Bridging'; return t(`button.start${transactionType}`); } } case RouteExecutionStatus.Failed: return t('button.tryAgain'); default: return ''; } }; return (_jsxs(PageContainer, { bottomGutters: true, children: [getStepList(route, subvariant), subvariant === 'custom' && contractSecondaryComponent ? (_jsx(ContractComponent, { sx: { marginTop: 2 }, children: contractSecondaryComponent })) : null, _jsx(TransactionDetails, { route: route, sx: { marginTop: 2 } }), status === RouteExecutionStatus.Idle || status === RouteExecutionStatus.Failed ? (_jsxs(_Fragment, { children: [_jsx(WarningMessages, { mt: 2, route: route, allowInteraction: true }), _jsxs(Box, { sx: { mt: 2, display: 'flex', }, children: [_jsx(StartTransactionButton, { text: getButtonText(), onClick: handleStartClick, route: route, loading: routeRefreshing || isLoadingAddressActivity }), status === RouteExecutionStatus.Failed ? (_jsx(Tooltip, { title: t('button.removeTransaction'), placement: "bottom-end", children: _jsx(Button, { onClick: handleRemoveRoute, sx: { minWidth: 48, marginLeft: 1, }, children: _jsx(Delete, {}) }) })) : null] })] })) : null, status ? _jsx(StatusBottomSheet, { status: status, route: route }) : null, subvariant !== 'custom' ? (_jsx(TokenValueBottomSheet, { route: route, ref: tokenValueBottomSheetRef, onContinue: handleExecuteRoute })) : null, _jsx(ExchangeRateBottomSheet, { ref: exchangeRateBottomSheetRef }), !hiddenUI?.includes(HiddenUI.LowAddressActivityConfirmation) ? (_jsx(ConfirmToAddressSheet, { ref: confirmToAddressSheetRef, onContinue: handleExecuteRoute, toAddress: toAddress, toChainId: route.toChainId })) : null] })); }; //# sourceMappingURL=TransactionPage.js.map