diff --git a/examples/deposit-flow/src/App.tsx b/examples/deposit-flow/src/App.tsx index 727a9ad74..4888e834b 100644 --- a/examples/deposit-flow/src/App.tsx +++ b/examples/deposit-flow/src/App.tsx @@ -4,19 +4,14 @@ import { CoinKey, DisabledUI, HiddenUI, - ItemPrice, LiFiWidget, } from '@lifi/widget' import { useMemo } from 'react' +import { DepositCard } from './components/DepositCard' +import { contractTool } from './config' -const depositAddress = '0xdde759c7cf032b1d0e633a7e9cfa6653d1911a22' -const depositAmount = 5000000n - -export const contractTool = { - logoURI: - 'https://github.com/lifinance/widget/assets/18644653/eb043a91-18ba-4da7-91c4-029a53a25989', - name: 'Immutable', -} +// EXAMPLE CONTRACT, DON'T DEPOSIT +const depositAddress = '0x4bF3E32de155359D1D75e8B474b66848221142fc' const contractCalls: ContractCall[] = [] @@ -30,7 +25,7 @@ export function App() { }, subvariant: 'custom', subvariantOptions: { custom: 'deposit' }, - integrator: 'Immutable', + integrator: 'ProtocolName', disabledUI: [DisabledUI.ToAddress], hiddenUI: [HiddenUI.Appearance, HiddenUI.Language], useRecommendedRoute: true, @@ -47,7 +42,7 @@ export function App() { return ( diff --git a/examples/deposit-flow/src/components/DepositCard.tsx b/examples/deposit-flow/src/components/DepositCard.tsx new file mode 100644 index 000000000..c3b1fac6d --- /dev/null +++ b/examples/deposit-flow/src/components/DepositCard.tsx @@ -0,0 +1,84 @@ +import { + type ContractCall, + type TokenAmount, + useFieldActions, +} from '@lifi/widget' +import { Avatar, Box, Chip, Stack, Typography } from '@mui/material' +import { useEffect } from 'react' +import { contractTool } from '../config' + +export interface ItemPriceProps { + token: TokenAmount + contractCalls?: ContractCall[] +} + +export const DepositCard: React.FC = ({ + token, + contractCalls, +}) => { + const { setFieldValue } = useFieldActions() + + useEffect(() => { + if (token) { + setFieldValue('toChain', token.chainId, { isTouched: true }) + setFieldValue('toToken', token.address, { isTouched: true }) + } + if (contractCalls) { + setFieldValue('contractCalls', contractCalls, { + isTouched: true, + }) + } + }, [contractCalls, setFieldValue, token]) + return ( + + + + + Protocol Name + + + + + TVL + + $69M + + + + Incentives + + $4.2M + + + + + } /> + + } + /> + + + ) +} diff --git a/examples/deposit-flow/src/config.ts b/examples/deposit-flow/src/config.ts new file mode 100644 index 000000000..4aa4da344 --- /dev/null +++ b/examples/deposit-flow/src/config.ts @@ -0,0 +1,4 @@ +export const contractTool = { + logoURI: 'https://gravatar.com/avatar/any?d=wavatar', + name: 'Protocol Name', +} diff --git a/examples/deposit-flow/src/main.tsx b/examples/deposit-flow/src/main.tsx index daaa3ad04..fd76452db 100644 --- a/examples/deposit-flow/src/main.tsx +++ b/examples/deposit-flow/src/main.tsx @@ -2,7 +2,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import React from 'react' import ReactDOM from 'react-dom/client' import { http, WagmiProvider, createConfig } from 'wagmi' -import { base, mainnet } from 'wagmi/chains' +import { arbitrum, base, mainnet, optimism } from 'wagmi/chains' import { injected } from 'wagmi/connectors' import { App } from './App' import { WalletHeader } from './components/WalletHeader' @@ -12,11 +12,13 @@ const queryClient = new QueryClient() const _projectId = import.meta.env.VITE_WALLET_CONNECT_PROJECT_ID const config = createConfig({ - chains: [mainnet], + chains: [mainnet, base, optimism, arbitrum], connectors: [injected()], transports: { [mainnet.id]: http(), [base.id]: http(), + [optimism.id]: http(), + [arbitrum.id]: http(), }, }) diff --git a/package.json b/package.json index acc6b2690..f1cfee050 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "3.12.6-alpha.0", + "version": "3.12.6-beta.0", "private": true, "sideEffects": false, "type": "module", diff --git a/packages/wallet-management/package.json b/packages/wallet-management/package.json index da3904f97..987855618 100644 --- a/packages/wallet-management/package.json +++ b/packages/wallet-management/package.json @@ -1,6 +1,6 @@ { "name": "@lifi/wallet-management", - "version": "3.4.8-alpha.0", + "version": "3.4.8-beta.0", "description": "LI.FI Wallet Management solution.", "type": "module", "main": "./src/index.ts", diff --git a/packages/wallet-management/src/i18n/i18next.d.ts b/packages/wallet-management/src/i18n/i18next.d.ts index 49e267140..ada8de6c2 100644 --- a/packages/wallet-management/src/i18n/i18next.d.ts +++ b/packages/wallet-management/src/i18n/i18next.d.ts @@ -2,13 +2,9 @@ import en from './en.json' with { type: 'json' } const defaultResource = { translation: en } -declare global { - namespace WalletTranslation { - declare module 'i18next' { - interface CustomTypeOptions { - defaultNS: 'translation' - resources: typeof defaultResource - } - } +declare module 'i18next' { + interface CustomTypeOptions { + defaultNS: 'translation' + resources: typeof defaultResource } } diff --git a/packages/widget-embedded/src/config.ts b/packages/widget-embedded/src/config.ts index cb0366583..c30152f8c 100644 --- a/packages/widget-embedded/src/config.ts +++ b/packages/widget-embedded/src/config.ts @@ -3,6 +3,7 @@ import './index.css' export const widgetBaseConfig: WidgetConfig = { subvariant: 'custom', + integrator: 'li.fi-playground', hiddenUI: ['history'], // buildUrl: true, diff --git a/packages/widget/package.json b/packages/widget/package.json index 40cf81e08..4b44eabb0 100644 --- a/packages/widget/package.json +++ b/packages/widget/package.json @@ -1,6 +1,6 @@ { "name": "@lifi/widget", - "version": "3.12.6-alpha.0", + "version": "3.12.6-beta.0", "description": "LI.FI Widget for cross-chain bridging and swapping. It will drive your multi-chain strategy and attract new users from everywhere.", "type": "module", "main": "./src/index.ts", diff --git a/packages/widget/src/components/AmountInput/AmountInput.tsx b/packages/widget/src/components/AmountInput/AmountInput.tsx index 603a9f42d..2ac23a3f6 100644 --- a/packages/widget/src/components/AmountInput/AmountInput.tsx +++ b/packages/widget/src/components/AmountInput/AmountInput.tsx @@ -70,6 +70,7 @@ export const AmountInputBase: React.FC< ...props }) => { const { t } = useTranslation() + const { subvariant, subvariantOptions } = useWidgetConfig() const ref = useRef(null) const amountKey = FormKeyHelper.getAmountKey(formType) const { onChange, onBlur, value } = useFieldController({ name: amountKey }) @@ -98,9 +99,16 @@ export const AmountInputBase: React.FC< } }, [value]) + const title = + subvariant === 'custom' + ? subvariantOptions?.custom === 'deposit' + ? t('header.amount') + : t('header.youPay') + : t('header.send') + return ( - {t('header.send')} + {title} diff --git a/packages/widget/src/components/ContractComponent/ItemPrice.tsx b/packages/widget/src/components/ContractComponent/ItemPrice.tsx index e02c83171..77899e541 100644 --- a/packages/widget/src/components/ContractComponent/ItemPrice.tsx +++ b/packages/widget/src/components/ContractComponent/ItemPrice.tsx @@ -1,5 +1,6 @@ import type { ContractCall } from '@lifi/sdk' import { useEffect } from 'react' +import { formatUnits } from 'viem' import { useFieldActions } from '../../stores/form/useFieldActions.js' import type { TokenAmount } from '../../types/token.js' import { Token } from '../Token/Token.js' @@ -19,9 +20,13 @@ export const ItemPrice: React.FC = ({ if (token) { setFieldValue('toChain', token.chainId, { isTouched: true }) setFieldValue('toToken', token.address, { isTouched: true }) - setFieldValue('toAmount', token.amount?.toString(), { - isTouched: true, - }) + setFieldValue( + 'toAmount', + token.amount ? formatUnits(token.amount, token.decimals) : '', + { + isTouched: true, + } + ) } if (contractCalls) { setFieldValue('contractCalls', contractCalls, { diff --git a/packages/widget/src/components/ContractComponent/NFT/NFT.tsx b/packages/widget/src/components/ContractComponent/NFT/NFT.tsx index adafc8eb0..5f66b1fdf 100644 --- a/packages/widget/src/components/ContractComponent/NFT/NFT.tsx +++ b/packages/widget/src/components/ContractComponent/NFT/NFT.tsx @@ -1,4 +1,5 @@ import { useEffect } from 'react' +import { formatUnits } from 'viem' import { useFieldActions } from '../../../stores/form/useFieldActions.js' import { NFTBase } from './NFTBase.js' import type { NFTProps } from './types.js' @@ -18,9 +19,13 @@ export const NFT: React.FC = ({ if (token) { setFieldValue('toChain', token.chainId, { isTouched: true }) setFieldValue('toToken', token.address, { isTouched: true }) - setFieldValue('toAmount', token.amount?.toString(), { - isTouched: true, - }) + setFieldValue( + 'toAmount', + token.amount ? formatUnits(token.amount, token.decimals) : '', + { + isTouched: true, + } + ) } if (contractCall) { setFieldValue('contractCalls', [contractCall], { diff --git a/packages/widget/src/components/RouteCard/RouteCard.tsx b/packages/widget/src/components/RouteCard/RouteCard.tsx index bf545d131..903e20d60 100644 --- a/packages/widget/src/components/RouteCard/RouteCard.tsx +++ b/packages/widget/src/components/RouteCard/RouteCard.tsx @@ -26,7 +26,7 @@ export const RouteCard: React.FC< ...other }) => { const { t } = useTranslation() - const { subvariant } = useWidgetConfig() + const { subvariant, subvariantOptions } = useWidgetConfig() const [cardExpanded, setCardExpanded] = useState(defaulExpanded) const handleExpand: MouseEventHandler = (e) => { @@ -35,7 +35,7 @@ export const RouteCard: React.FC< } const token: TokenAmount = - subvariant === 'custom' + subvariant === 'custom' && subvariantOptions?.custom !== 'deposit' ? { ...route.fromToken, amount: BigInt(route.fromAmount) } : { ...route.toToken, amount: BigInt(route.toAmount) } const impactToken: TokenAmount | undefined = diff --git a/packages/widget/src/components/Routes/Routes.tsx b/packages/widget/src/components/Routes/Routes.tsx index dc3777c5d..82881e45b 100644 --- a/packages/widget/src/components/Routes/Routes.tsx +++ b/packages/widget/src/components/Routes/Routes.tsx @@ -16,7 +16,8 @@ import { RouteNotFoundCard } from '../RouteCard/RouteNotFoundCard.js' export const Routes: React.FC = (props) => { const { t } = useTranslation() const navigate = useNavigate() - const { subvariant, useRecommendedRoute } = useWidgetConfig() + const { subvariant, subvariantOptions, useRecommendedRoute } = + useWidgetConfig() const { routes, isLoading, @@ -42,11 +43,16 @@ export const Routes: React.FC = (props) => { const showAll = !onlyRecommendedRoute && !routeNotFound && (routes?.length ?? 0) > 1 + const title = + subvariant === 'custom' + ? subvariantOptions?.custom === 'deposit' + ? t('header.receive') + : t('header.youPay') + : t('header.receive') + return ( - - {subvariant === 'custom' ? t('header.youPay') : t('header.receive')} - + {title} { export const RoutesExpandedElement = () => { const { t } = useTranslation() const navigate = useNavigate() - const { subvariant } = useWidgetConfig() + const { subvariant, subvariantOptions } = useWidgetConfig() const routesRef = useRef() const emitter = useWidgetEvents() const routesActiveRef = useRef(false) @@ -118,6 +118,13 @@ export const RoutesExpandedElement = () => { emitter.emit(WidgetEvent.WidgetExpanded, expanded) }, [emitter, expanded]) + const title = + subvariant === 'custom' + ? subvariantOptions?.custom === 'deposit' + ? t('header.deposit') + : t('header.youPay') + : t('header.receive') + return ( { flex: 1, }} > - {subvariant === 'custom' - ? t('header.youPay') - : t('header.receive')} + {title} = (props) => { const { t } = useTranslation() const navigate = useNavigate() - const { disabledUI, hiddenUI, toAddress, toAddresses } = useWidgetConfig() + const { + disabledUI, + hiddenUI, + toAddress, + toAddresses, + subvariant, + subvariantOptions, + } = useWidgetConfig() const { showSendToWallet } = useSendToWalletStore() const [toAddressFieldValue, toChainId, toTokenAddress] = useFieldValues( 'toAddress', @@ -114,6 +121,11 @@ export const SendToWalletButton: React.FC = (props) => { const isOpenCollapse = requiredToAddress || (showSendToWallet && !hiddenToAddress) + const title = + subvariant === 'custom' && subvariantOptions?.custom === 'deposit' + ? t('header.depositTo') + : t('header.sendToWallet') + return ( = (props) => { sx={{ width: '100%', ...props.sx }} > - {t('header.sendToWallet')} + {title} { const toChain = getChainById(toChainId) const fromChain = getChainById(fromChainId) - const { accounts } = useAccount() + const { account: toAccount } = useAccount({ chainType: toChain?.chainType }) - const fromAccount = accounts.find( - (account) => account.chainType === fromChain?.chainType - ) - - const toAccount = accounts.find( - (account) => account.chainType === toChain?.chainType - ) + const effectiveToAddress = toAddress || toAccount?.address - const isFromContractAddress = useIsContractAddress( - fromAccount?.address, - fromChainId, - fromAccount?.chainType - ) const isToContractAddress = useIsContractAddress( - toAddress, + effectiveToAddress, toChainId, toChain?.chainType ) const { token: destinationNativeToken } = useTokenBalance( - toAddress || toAccount?.address, + effectiveToAddress, toChainId ? toChain?.nativeToken : undefined ) @@ -55,9 +44,8 @@ export const useGasRefuel = () => { const isChainTypeSatisfied = fromChain?.chainType !== toChain?.chainType ? Boolean(toAddress) : true - const isToAddressSatisfied = isFromContractAddress - ? toAddress && toAddress !== fromAccount?.address && !isToContractAddress - : true + // We should not refuel to the contract address + const isToAddressSatisfied = effectiveToAddress && !isToContractAddress const enabled = useMemo(() => { if ( diff --git a/packages/widget/src/hooks/useIsContractAddress.ts b/packages/widget/src/hooks/useIsContractAddress.ts index 36def167a..fb0b6e6b7 100644 --- a/packages/widget/src/hooks/useIsContractAddress.ts +++ b/packages/widget/src/hooks/useIsContractAddress.ts @@ -16,6 +16,7 @@ export const useIsContractAddress = ( enabled: Boolean(chainType === ChainType.EVM && chainId), }, }) + const isContractAddress = !!contractCode return isContractAddress } diff --git a/packages/widget/src/hooks/useRoutes.ts b/packages/widget/src/hooks/useRoutes.ts index 9a34de458..3abd62254 100644 --- a/packages/widget/src/hooks/useRoutes.ts +++ b/packages/widget/src/hooks/useRoutes.ts @@ -177,6 +177,7 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => { signal, }) => { const fromAmount = parseUnits(fromTokenAmount, fromToken!.decimals) + const toAmount = parseUnits(toTokenAmount, toToken!.decimals) const formattedSlippage = Number.parseFloat(slippage) / 100 const allowBridges = swapOnly @@ -210,17 +211,18 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => { fromAddress, toAddress, fromAmount, + toAmount, slippage: formattedSlippage, }) - if (subvariant === 'custom' && contractCalls && toTokenAmount) { + if (subvariant === 'custom' && contractCalls && toAmount) { const contractCallQuote = await getContractCallsQuote( { // Contract calls are enabled only when fromAddress is set fromAddress: fromAddress as string, fromChain: fromChainId, fromToken: fromTokenAddress, - toAmount: toTokenAmount, + toAmount: toAmount.toString(), toChain: toChainId, toToken: toTokenAddress, contractCalls, @@ -265,8 +267,8 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => { fromAddress: contractCallQuote.action.fromAddress, toChainId: contractCallQuote.action.toChainId, toAmountUSD: contractCallQuote.estimate.toAmountUSD || '', - toAmount: toTokenAmount, - toAmountMin: toTokenAmount, + toAmount: contractCallQuote.estimate.toAmount, + toAmountMin: contractCallQuote.estimate.toAmountMin, toToken: toToken!, toAddress: contractCallQuote.action.toAddress || diff --git a/packages/widget/src/i18n/en.json b/packages/widget/src/i18n/en.json index a0d8dffa2..51a57ea24 100644 --- a/packages/widget/src/i18n/en.json +++ b/packages/widget/src/i18n/en.json @@ -52,12 +52,14 @@ }, "header": { "activeTransactions": "Active transactions", + "amount": "Amount", "bookmarkedWallets": "Bookmarked wallets", "bridge": "Bridge", "checkout": "Checkout", "checkoutDetails": "Checkout details", "deposit": "Deposit", "depositDetails": "Deposit details", + "depositTo": "Deposit to", "exchange": "Exchange", "from": "Exchange from", "gas": "Gas", @@ -265,13 +267,15 @@ "stepBridge": "Bridge", "stepBridgeAndBuy": "Bridge and buy", "stepBridgeAndDeposit": "Bridge and deposit", + "stepBuy": "Buy", + "stepDeposit": "Deposit", "stepDetails": "{{tool}} via LI.FI", "stepSwap": "Swap", "stepSwapAndBridge": "Swap and bridge", "stepSwapAndBuy": "Swap and buy", "stepSwapAndDeposit": "Swap and deposit", - "transferId": "Transfer ID", "swapStepDetails": "Swap on {{chain}} via {{tool}}", + "transferId": "Transfer ID", "tags": { "cheapest": "Best Return", "fastest": "Fastest" diff --git a/packages/widget/src/i18n/i18next.d.ts b/packages/widget/src/i18n/i18next.d.ts index 509d0fd72..ada8de6c2 100644 --- a/packages/widget/src/i18n/i18next.d.ts +++ b/packages/widget/src/i18n/i18next.d.ts @@ -2,13 +2,9 @@ import en from './en.json' with { type: 'json' } const defaultResource = { translation: en } -declare global { - namespace GeneralTranslation { - declare module 'i18next' { - interface CustomTypeOptions { - defaultNS: 'translation' - resources: typeof defaultResource - } - } +declare module 'i18next' { + interface CustomTypeOptions { + defaultNS: 'translation' + resources: typeof defaultResource } } diff --git a/packages/widget/src/index.ts b/packages/widget/src/index.ts index 31d7275d0..337237a9d 100644 --- a/packages/widget/src/index.ts +++ b/packages/widget/src/index.ts @@ -1,5 +1,5 @@ export type * from '@lifi/sdk' -export { ChainType, ChainId } from '@lifi/sdk' +export { ChainType, ChainId, CoinKey } from '@lifi/sdk' export { App as LiFiWidget } from './App.js' export type { WidgetDrawer } from './AppDrawer.js' export * from './components/ContractComponent/ItemPrice.js' diff --git a/packages/widget/src/pages/MainPage/MainPage.tsx b/packages/widget/src/pages/MainPage/MainPage.tsx index f6b290941..d4fdc5327 100644 --- a/packages/widget/src/pages/MainPage/MainPage.tsx +++ b/packages/widget/src/pages/MainPage/MainPage.tsx @@ -31,22 +31,23 @@ export const MainPage: React.FC = () => { : subvariant === 'refuel' ? t('header.gas') : t('header.exchange') + useHeader(title) + const marginSx = { marginBottom: 2 } + return ( - + {custom ? ( - - {contractComponent} - + {contractComponent} ) : null} - {!custom ? ( - + {!custom || subvariantOptions?.custom === 'deposit' ? ( + ) : null} - {!wideVariant ? : null} - + {!wideVariant ? : null} +