diff --git a/packages/widget/src/components/StepActions/StepActions.tsx b/packages/widget/src/components/StepActions/StepActions.tsx index 841359bc8..ac1fbaa91 100644 --- a/packages/widget/src/components/StepActions/StepActions.tsx +++ b/packages/widget/src/components/StepActions/StepActions.tsx @@ -104,7 +104,7 @@ export const StepActions: React.FC = ({ }; export const IncludedSteps: React.FC = ({ step }) => { - const { subvariant, subvariantOptions, feeTool, hiddenUI } = + const { subvariant, subvariantOptions, feeConfig, hiddenUI } = useWidgetConfig(); let includedSteps = step.includedSteps; @@ -127,8 +127,8 @@ export const IncludedSteps: React.FC = ({ step }) => { const tool = includedStep?.type === 'protocol' && includedStep?.tool === 'feeCollection' && - feeTool - ? feeTool + feeConfig + ? feeConfig : includedStep?.toolDetails; return tool ? ( = ({ step }) => { ) : step.type === 'cross' ? ( ) : step.type === 'protocol' ? ( - + ) : ( )} @@ -315,13 +315,13 @@ export const SwapStepDetailsLabel: React.FC< export const ProtocolStepDetailsLabel: React.FC< Omit -> = ({ step, feeTool }) => { +> = ({ step, feeConfig }) => { const { t } = useTranslation(); return ( {step.toolDetails.key === 'feeCollection' - ? feeTool?.name - ? t('main.fees.integrator', { tool: feeTool.name }) + ? feeConfig?.name + ? t('main.fees.integrator', { tool: feeConfig.name }) : t('main.fees.defaultIntegrator') : step.toolDetails.key === 'lifuelProtocol' ? t('main.refuelStepDetails', { diff --git a/packages/widget/src/components/StepActions/types.ts b/packages/widget/src/components/StepActions/types.ts index f0e1e8bda..aa0a958b7 100644 --- a/packages/widget/src/components/StepActions/types.ts +++ b/packages/widget/src/components/StepActions/types.ts @@ -2,7 +2,7 @@ import type { LiFiStep, Step } from '@lifi/sdk'; import type { BoxProps } from '@mui/material'; import type { SubvariantOptions, - WidgetFeeTool, + WidgetFeeConfig, WidgetSubvariant, } from '../../types/widget.js'; @@ -15,7 +15,7 @@ export interface StepDetailsLabelProps { step: Step; subvariant?: Extract; subvariantOptions?: SubvariantOptions; - feeTool?: WidgetFeeTool; + feeConfig?: WidgetFeeConfig; } export interface IncludedStepsProps { diff --git a/packages/widget/src/components/TransactionDetails.tsx b/packages/widget/src/components/TransactionDetails.tsx index ebc8f4020..22edfbbd0 100644 --- a/packages/widget/src/components/TransactionDetails.tsx +++ b/packages/widget/src/components/TransactionDetails.tsx @@ -32,7 +32,7 @@ export const TransactionDetails: React.FC = ({ ...props }) => { const { t } = useTranslation(); - const { feeTool } = useWidgetConfig(); + const { feeConfig } = useWidgetConfig(); const [cardExpanded, setCardExpanded] = useState(false); const toggleCard = () => { @@ -154,13 +154,13 @@ export const TransactionDetails: React.FC = ({ {feeAmountUSD ? ( - {feeTool?.name - ? t('main.fees.integrator', { tool: feeTool.name }) + {feeConfig?.name + ? t('main.fees.integrator', { tool: feeConfig.name }) : t('main.fees.defaultIntegrator')} - {feeTool?.name ? ( + {feeConfig?.name ? ( diff --git a/packages/widget/src/hooks/useRoutes.ts b/packages/widget/src/hooks/useRoutes.ts index 96fe324bb..980ce8a06 100644 --- a/packages/widget/src/hooks/useRoutes.ts +++ b/packages/widget/src/hooks/useRoutes.ts @@ -25,8 +25,15 @@ interface RoutesProps { } export const useRoutes = ({ observableRoute }: RoutesProps = {}) => { - const { subvariant, sdkConfig, contractTool, bridges, exchanges } = - useWidgetConfig(); + const { + subvariant, + sdkConfig, + contractTool, + bridges, + exchanges, + fee, + feeConfig, + } = useWidgetConfig(); const setExecutableRoute = useSetExecutableRoute(); const queryClient = useQueryClient(); const emitter = useWidgetEvents(); @@ -137,6 +144,7 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => { sdkConfig?.routeOptions?.allowSwitchChain, enabledRefuel && enabledAutoRefuel, gasRecommendationFromAmount, + feeConfig?.fee || fee, observableRoute?.id, ] as const; @@ -166,14 +174,12 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => { allowSwitchChain, enabledRefuel, gasRecommendationFromAmount, + fee, observableRouteId, ], signal, }) => { - const fromAmount = parseUnits( - fromTokenAmount, - fromToken!.decimals, - ).toString(); + const fromAmount = parseUnits(fromTokenAmount, fromToken!.decimals); const formattedSlippage = parseFloat(slippage) / 100; const allowBridges = swapOnly @@ -199,6 +205,17 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => { ) : allowedExchanges; + const calculatedFee = await feeConfig?.calculateFee?.({ + fromChainId, + toChainId, + fromTokenAddress, + toTokenAddress, + fromAddress, + toAddress, + fromAmount, + slippage: formattedSlippage, + }); + if (subvariant === 'custom' && contractCalls && toTokenAmount) { const contractCallQuote = await getContractCallsQuote( { @@ -218,6 +235,7 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => { allowExchanges, toFallbackAddress: toAddress, slippage: formattedSlippage, + fee: calculatedFee || fee, }, { signal }, ); @@ -267,7 +285,7 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => { const data = await getRoutes( { fromAddress, - fromAmount, + fromAmount: fromAmount.toString(), fromChainId, fromTokenAddress, toAddress, @@ -300,6 +318,7 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => { : undefined, order: routePriority, slippage: formattedSlippage, + fee: calculatedFee || fee, }, }, { signal }, diff --git a/packages/widget/src/providers/WidgetProvider/WidgetProvider.tsx b/packages/widget/src/providers/WidgetProvider/WidgetProvider.tsx index 1328db5ef..dbb23ad3f 100644 --- a/packages/widget/src/providers/WidgetProvider/WidgetProvider.tsx +++ b/packages/widget/src/providers/WidgetProvider/WidgetProvider.tsx @@ -80,7 +80,7 @@ export const WidgetProvider: React.FC< apiKey: widgetConfig.apiKey, integrator: widgetConfig.integrator ?? window?.location.hostname, routeOptions: { - fee: widgetConfig.fee, + fee: widgetConfig.feeConfig?.fee || widgetConfig.fee, referrer: widgetConfig.referrer, order: widgetConfig.routePriority, slippage: widgetConfig.slippage, diff --git a/packages/widget/src/stores/routes/createRouteExecutionStore.ts b/packages/widget/src/stores/routes/createRouteExecutionStore.ts index 32461b08d..143ae2de7 100644 --- a/packages/widget/src/stores/routes/createRouteExecutionStore.ts +++ b/packages/widget/src/stores/routes/createRouteExecutionStore.ts @@ -131,7 +131,7 @@ export const createRouteExecutionStore = ({ namePrefix }: PersistStoreProps) => }), { name: `${namePrefix || 'li.fi'}-widget-routes`, - version: 1, + version: 2, partialize: (state) => ({ routes: state.routes }), merge: (persistedState: any, currentState: RouteExecutionState) => { const state = { diff --git a/packages/widget/src/types/widget.ts b/packages/widget/src/types/widget.ts index 5edc6c12e..ece308199 100644 --- a/packages/widget/src/types/widget.ts +++ b/packages/widget/src/types/widget.ts @@ -111,13 +111,34 @@ export interface WidgetSDKConfig } export interface WidgetContractTool { - logoURI: string; name: string; + logoURI: string; } -export interface WidgetFeeTool { - logoURI: string; - name: string; +export interface CalculateFeeParams { + fromChainId: number; + toChainId: number; + fromTokenAddress: string; + toTokenAddress: string; + fromAddress?: string; + toAddress?: string; + fromAmount: bigint; + slippage: number; +} + +export interface WidgetFeeConfig { + name?: string; + logoURI?: string; + fee?: number; + /** + * Function to calculate fees before fetching quotes. + * If provided, this function will be used instead of the `fee` parameter. + * Only one of `fee` or `calculateFee` should be used. + * + * @param params Object containing the fee calculation parameters + * @returns A promise that resolves to the calculated fee as a number (e.g., 0.03 represents a 3% fee) + */ + calculateFee?(params: CalculateFeeParams): Promise; } export interface ToAddress { @@ -150,8 +171,11 @@ export interface WidgetConfig { integrator: string; apiKey?: string; + /** + * @deprecated Use `feeConfig` instead. + */ fee?: number; - feeTool?: WidgetFeeTool; + feeConfig?: WidgetFeeConfig; referrer?: string; routePriority?: Order;