import type { LiFiStep, StepExtended } from '@lifi/sdk'; import { ArrowForward, ExpandLess, ExpandMore } from '@mui/icons-material'; import type { StepIconProps } from '@mui/material'; import { Badge, Box, Collapse, Step as MuiStep, Stepper, Typography, } from '@mui/material'; import type { MouseEventHandler } from 'react'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { formatUnits } from 'viem'; import { useAvailableChains } from '../../hooks/useAvailableChains.js'; import { LiFiToolLogo } from '../../icons/lifi.js'; import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'; import { HiddenUI } from '../../types/widget.js'; import { formatTokenAmount } from '../../utils/format.js'; import { CardIconButton } from '../Card/CardIconButton.js'; import { SmallAvatar } from '../SmallAvatar.js'; import { StepAvatar, StepConnector, StepContent, StepLabel, StepLabelTypography, } from './StepActions.style.js'; import { StepFees } from './StepFees.js'; import type { IncludedStepsProps, StepActionsProps, StepDetailsLabelProps, } from './types.js'; import { ChainId } from '../../types/base.js'; import { safeBigInt } from '../../utils/utils.js'; export const StepActions: React.FC = ({ step, dense, ...other }) => { const { t } = useTranslation(); const { subvariant, // brandingLogo } = useWidgetConfig(); const [cardExpanded, setCardExpanded] = useState(false); const handleExpand: MouseEventHandler = (e) => { e.stopPropagation(); Promise.resolve().then(() => { setCardExpanded((expanded) => !expanded); }); }; // FIXME: step transaction request overrides step tool details, but not included step tool details const toolDetails = subvariant === 'custom' ? step?.includedSteps?.find( (step) => step.tool === 'custom' && step.toolDetails.key !== 'custom', )?.toolDetails || step.toolDetails : step.toolDetails; return ( // } > {toolDetails?.name?.[0]} {toolDetails?.name?.includes('LI.FI') ? toolDetails.name : t(`main.stepDetails`, { tool: toolDetails?.name, })} {dense ? ( {cardExpanded ? ( ) : ( )} ) : null} {dense ? ( ) : ( )} ); }; export const IncludedSteps: React.FC = ({ step }) => { const { t } = useTranslation(); const { subvariant, subvariantOptions, feeConfig, hiddenUI, // commissionBps, commissionBpsSDK, } = useWidgetConfig(); let includedSteps = step.includedSteps; if (hiddenUI?.includes(HiddenUI.IntegratorStepDetails)) { const feeCollectionStep = includedSteps.find( (step) => step.tool === 'feeCollection', ); if (feeCollectionStep) { includedSteps = structuredClone( includedSteps.filter((step) => step.tool !== 'feeCollection'), ); includedSteps[0].estimate.fromAmount = feeCollectionStep.estimate.fromAmount; } } // eslint-disable-next-line react/no-unstable-nested-components const StepIconComponent = ({ icon }: StepIconProps) => { const includedStep = includedSteps?.[Number(icon) - 1]; const feeCollectionStep = includedStep?.type === 'protocol' && includedStep?.tool === 'feeCollection'; const toolName = feeCollectionStep && feeConfig?.name ? feeConfig?.name : includedStep?.toolDetails?.name; const toolLogoURI = feeCollectionStep && feeConfig?.logoURI ? feeConfig?.logoURI : includedStep?.toolDetails.logoURI; return toolLogoURI ? ( {toolName?.[0]} ) : null; }; return ( } activeStep={-1} > {(includedSteps || [])?.map((step, i, includedSteps) => ( {step.type === 'custom' && subvariant === 'custom' ? ( ) : step.type === 'cross' ? ( ) : step.type === 'protocol' ? ( ) : ( )} ))} {commissionBpsSDK && commissionBpsSDK[ChainId.MOVE] ? ( Swap Fee {t('format.percent', { value: commissionBpsSDK && commissionBpsSDK[ChainId.MOVE] && commissionBpsSDK[ChainId.MOVE] / 10000, })} ) : null} ); }; export const StepDetailsContent: React.FC<{ step: StepExtended; }> = ({ step }) => { const { t } = useTranslation(); const sameTokenProtocolStep = step.action.fromToken?.chainId === step.action.toToken?.chainId && step.action.fromToken?.address === step.action.toToken?.address; let fromAmount: string | undefined; if (sameTokenProtocolStep) { const estimatedFromAmount = safeBigInt(step?.estimate?.fromAmount) - safeBigInt(step?.estimate?.toAmount); fromAmount = estimatedFromAmount > 0n ? formatUnits( estimatedFromAmount, step?.action?.fromToken?.decimals || 0, ) : formatUnits( safeBigInt(step?.estimate?.fromAmount), step?.action?.fromToken?.decimals || 0, ); } else { fromAmount = formatTokenAmount( safeBigInt(step?.estimate?.fromAmount), step?.action?.fromToken?.decimals || 0, ); } const showToAmount = step.type !== 'custom' && step.tool !== 'custom' && !sameTokenProtocolStep; return ( {!showToAmount ? ( <> {formatUnits( safeBigInt(step?.estimate?.fromAmount), step?.action?.fromToken?.decimals || 0, )}{' '} {step?.action?.fromToken?.symbol} {' - '} ) : null} {t('format.number', { value: fromAmount, })}{' '} {step?.action?.fromToken?.symbol} {showToAmount ? ( <> {t('format.number', { value: formatTokenAmount( safeBigInt(step?.execution?.toAmount ?? step?.estimate?.toAmount), step.execution?.toToken?.decimals ?? (step.action.toToken?.decimals || 0), ), })}{' '} {step.execution?.toToken?.symbol ?? step.action.toToken?.symbol} ) : ( ` (${t('format.currency', { value: parseFloat(fromAmount || '0') * parseFloat(step?.action?.fromToken?.priceUSD || '0'), })})` )} ); }; export const CustomStepDetailsLabel: React.FC = ({ step, subvariant, subvariantOptions, }) => { const { t } = useTranslation(); if (!subvariant) { return null; } // FIXME: step transaction request overrides step tool details, but not included step tool details const toolDetails = subvariant === 'custom' && (step as unknown as LiFiStep).includedSteps?.length > 0 ? (step as unknown as LiFiStep).includedSteps.find( (step) => step.tool === 'custom' && step.toolDetails.key !== 'custom', )?.toolDetails || step.toolDetails : step.toolDetails; const stepDetailsKey = (subvariant === 'custom' && subvariantOptions?.custom) || 'checkout'; return ( {t(`main.${stepDetailsKey}StepDetails`, { tool: toolDetails.name, })} ); }; export const BridgeStepDetailsLabel: React.FC< Omit > = ({ step }) => { const { t } = useTranslation(); const { getChainById } = useAvailableChains(); return ( {t('main.bridgeStepDetails', { from: getChainById(step.action.fromChainId)?.name, to: getChainById(step.action.toChainId)?.name, tool: step.toolDetails.name, })} ); }; export const SwapStepDetailsLabel: React.FC< Omit > = ({ step }) => { const { t } = useTranslation(); const { getChainById } = useAvailableChains(); return ( {t('main.swapStepDetails', { chain: getChainById(step.action.fromChainId)?.name, tool: step.toolDetails.name, })} ); }; export const ProtocolStepDetailsLabel: React.FC< Omit > = ({ step, feeConfig }) => { const { t } = useTranslation(); return ( {step.toolDetails.key === 'feeCollection' ? feeConfig?.name ? t('main.fees.integrator', { tool: feeConfig.name }) : t('main.fees.defaultIntegrator') : step.toolDetails.key === 'lifuelProtocol' ? t('main.refuelStepDetails', { tool: step.toolDetails.name, }) : step.toolDetails.name} ); };