From 92250e9a95a5d979de0ca85b1a88ddc7371c38c2 Mon Sep 17 00:00:00 2001 From: vutuanlinh2k2 <69841784+vutuanlinh2k2@users.noreply.github.com> Date: Fri, 3 Nov 2023 04:10:49 +0700 Subject: [PATCH] Bridge Tx Confirmation V2 (#1812) --- .../src/components/RelayerFeeDetails.tsx | 31 +- apps/bridge-dapp/src/components/index.ts | 1 - .../DepositConfirmContainer.tsx | 87 ++- .../DepositConfirmContainer/types.ts | 11 +- .../TransferConfirmContainer.tsx | 87 ++- .../TxInfoContainer/TxInfoContainer.tsx | 18 +- .../WithdrawConfirmContainer.tsx | 104 ++- apps/bridge-dapp/src/hooks/index.ts | 2 + .../useTransferFeeCalculation.ts} | 11 +- .../useWithdrawFeeCalculation.ts} | 17 +- .../src/pages/Hubble/Bridge/Deposit/index.tsx | 8 +- .../Deposit/private/useDepositButtonProps.tsx | 10 +- .../pages/Hubble/Bridge/Transfer/index.tsx | 11 +- .../pages/Hubble/Bridge/Withdraw/index.tsx | 11 +- .../dapp-config/src/utils/parseOnChainData.ts | 4 +- libs/icons/src/ChainIcon.tsx | 1 + libs/icons/src/EyeClosedLine.tsx | 13 + libs/icons/src/EyeLine.tsx | 13 + libs/icons/src/FileShieldLine.tsx | 13 + libs/icons/src/chains/athena.svg | 10 +- libs/icons/src/chains/demeter.svg | 10 +- libs/icons/src/chains/hermes.svg | 10 +- libs/icons/src/index.ts | 3 + .../components/AddressChip/AddressChip.tsx | 40 ++ .../src/components/AddressChip/index.ts | 4 + .../src/components/AddressChip/types.ts | 5 + .../CopyWithTooltip/CopyWithTooltip.tsx | 16 +- .../src/components/CopyWithTooltip/types.ts | 23 +- .../IconWithTooltip/IconWithTooltip.tsx | 12 +- .../TitleWithInfo/TitleWithInfo.tsx | 4 +- .../src/components/TokenWithAmount/types.ts | 5 + .../src/components/TokensRing/TokensRing.tsx | 599 ------------------ .../src/components/TokensRing/index.ts | 1 - .../src/components/TokensRing/types.ts | 45 -- .../TxConfirmationRing/TxConfirmationRing.tsx | 347 ++++++++++ .../components/TxConfirmationRing/index.ts | 4 + .../components/TxConfirmationRing/types.ts | 14 + .../components/TxProgressor/TxProgressor.tsx | 57 +- .../src/components/TxProgressor/types.ts | 5 + .../src/components/index.ts | 4 +- .../ConfirmationCard/AmountInfo.tsx | 61 ++ .../ConfirmationCard/DepositConfirm.tsx | 228 +++---- .../ConfirmationCard/RefundAmount.tsx | 86 +++ .../ConfirmationCard/SpendNoteInput.tsx | 48 ++ .../ConfirmationCard/TransferConfirm.tsx | 394 ++++-------- .../ConfirmationCard/WithdrawConfirm.tsx | 366 ++++------- .../ConfirmationCard/WrapperSection.tsx | 19 +- .../src/containers/ConfirmationCard/index.ts | 1 - .../src/containers/ConfirmationCard/types.ts | 75 ++- .../src/containers/ConfirmationCard/utils.ts | 24 +- .../stories/molecules/AddressChip.stories.jsx | 19 + .../stories/molecules/TokenRing.stories.jsx | 24 - .../molecules/TxConfirmationRing.stories.jsx | 23 + 53 files changed, 1445 insertions(+), 1594 deletions(-) rename apps/bridge-dapp/src/{pages/Hubble/Bridge/Transfer/private/useFeeCalculation.ts => hooks/useTransferFeeCalculation.ts} (94%) rename apps/bridge-dapp/src/{pages/Hubble/Bridge/Withdraw/private/useFeeCalculation.ts => hooks/useWithdrawFeeCalculation.ts} (92%) create mode 100644 libs/icons/src/EyeClosedLine.tsx create mode 100644 libs/icons/src/EyeLine.tsx create mode 100644 libs/icons/src/FileShieldLine.tsx create mode 100644 libs/webb-ui-components/src/components/AddressChip/AddressChip.tsx create mode 100644 libs/webb-ui-components/src/components/AddressChip/index.ts create mode 100644 libs/webb-ui-components/src/components/AddressChip/types.ts delete mode 100644 libs/webb-ui-components/src/components/TokensRing/TokensRing.tsx delete mode 100644 libs/webb-ui-components/src/components/TokensRing/index.ts delete mode 100644 libs/webb-ui-components/src/components/TokensRing/types.ts create mode 100644 libs/webb-ui-components/src/components/TxConfirmationRing/TxConfirmationRing.tsx create mode 100644 libs/webb-ui-components/src/components/TxConfirmationRing/index.ts create mode 100644 libs/webb-ui-components/src/components/TxConfirmationRing/types.ts create mode 100644 libs/webb-ui-components/src/containers/ConfirmationCard/AmountInfo.tsx create mode 100644 libs/webb-ui-components/src/containers/ConfirmationCard/RefundAmount.tsx create mode 100644 libs/webb-ui-components/src/containers/ConfirmationCard/SpendNoteInput.tsx create mode 100644 libs/webb-ui-components/src/stories/molecules/AddressChip.stories.jsx delete mode 100644 libs/webb-ui-components/src/stories/molecules/TokenRing.stories.jsx create mode 100644 libs/webb-ui-components/src/stories/molecules/TxConfirmationRing.stories.jsx diff --git a/apps/bridge-dapp/src/components/RelayerFeeDetails.tsx b/apps/bridge-dapp/src/components/RelayerFeeDetails.tsx index 0accc50d73..39a63d57b8 100644 --- a/apps/bridge-dapp/src/components/RelayerFeeDetails.tsx +++ b/apps/bridge-dapp/src/components/RelayerFeeDetails.tsx @@ -8,11 +8,11 @@ import { FeeDetails } from '@webb-tools/webb-ui-components/components/FeeDetails import type { FeeItem } from '@webb-tools/webb-ui-components/components/FeeDetails/types'; import { Typography } from '@webb-tools/webb-ui-components/typography/Typography'; import { formatEther } from 'viem'; -import getRelayerFeePercentage from '../utils/getRelayerFeePercentage'; -import { useMemo } from 'react'; +import { getRelayerFeePercentage } from '../utils'; +import { type FC, useMemo } from 'react'; import { calculateTypedChainId } from '@webb-tools/sdk-core/typed-chain-id'; -const RelayerFeeDetails = (props: { +interface RelayerFeeDetailsProps { totalFeeWei: bigint | undefined; totalFeeToken: string | undefined; gasFeeInfo: bigint | undefined; @@ -21,18 +21,20 @@ const RelayerFeeDetails = (props: { srcChainCfg: ChainConfig | undefined; fungibleCfg: CurrencyConfig | undefined; activeRelayer: OptionalActiveRelayer; -}) => { - const { - activeRelayer, - fungibleCfg, - gasFeeInfo, - isFeeLoading, - relayerFeeInfo, - srcChainCfg, - totalFeeToken, - totalFeeWei, - } = props; + info?: string; +} +const RelayerFeeDetails: FC = ({ + activeRelayer, + fungibleCfg, + gasFeeInfo, + isFeeLoading, + relayerFeeInfo, + srcChainCfg, + totalFeeToken, + totalFeeWei, + info, +}) => { const relayerFeePercentage = useMemo(() => { if (!activeRelayer || !srcChainCfg) { return; @@ -48,6 +50,7 @@ const RelayerFeeDetails = (props: { return ( { return new Currency(apiConfig.currencies[fungibleTokenId]); }, [apiConfig.currencies, fungibleTokenId]); @@ -83,14 +88,40 @@ const DepositConfirmContainer = forwardRef< txStatusMessage, } = useInProgressTxInfo(wrappingFlow, onResetState); - // Download for the deposit confirm - const downloadNote = useCallback((note: Note) => { - const noteStr = note.serialize(); - downloadString( - JSON.stringify(noteStr), - noteStr.slice(-noteStr.length) + '.json' - ); - }, []); + const sourceTypedChainId = useMemo( + () => sourceTypedChainIdProp ?? +note.note.sourceChainId, + [sourceTypedChainIdProp, note.note.sourceChainId] + ); + + const destTypedChainId = useMemo( + () => destTypedChainIdProp ?? +note.note.targetChainId, + [destTypedChainIdProp, note.note.targetChainId] + ); + + const newBalance = useMemo(() => { + const balance = balances?.[fungibleTokenId]?.[destTypedChainId]; + if (!balance) return amount; + return Number(formatEther(balance)) + amount; + }, [balances, fungibleTokenId, destTypedChainId, note, amount]); + + const poolAddress = useMemo( + () => apiConfig.anchors[fungibleTokenId][destTypedChainId], + [apiConfig, fungibleTokenId, destTypedChainId] + ); + + const poolExplorerUrl = useMemo(() => { + const blockExplorerUrl = + chainsConfig[destTypedChainId]?.blockExplorers?.default.url; + + if (!blockExplorerUrl) return undefined; + + return getExplorerURI( + blockExplorerUrl, + poolAddress, + 'address', + 'web3' + ).toString(); + }, [destTypedChainId, poolAddress]); const handleExecuteDeposit = useCallback( async () => { @@ -185,8 +216,7 @@ const DepositConfirmContainer = forwardRef< const transactionHash = await api.transact(...args); - downloadNote(note); - await addNoteToNoteManager(note); + await handleStoreNote(note, addNoteToNoteManager); enqueueSubmittedTx( transactionHash, @@ -236,7 +266,7 @@ const DepositConfirmContainer = forwardRef< } }, // prettier-ignore - [activeAccount?.address, activeApi, activeChain, addNoteToNoteManager, api, apiConfig, downloadNote, enqueueSubmittedTx, fungibleTokenId, inProgressTxId.length, note, onResetState, removeNoteFromNoteManager, setInProgressTxId, setTotalStep, startNewTransaction, txQueueApi, wrappableToken] + [activeAccount?.address, activeApi, activeChain, addNoteToNoteManager, api, apiConfig, enqueueSubmittedTx, fungibleTokenId, inProgressTxId.length, note, onResetState, removeNoteFromNoteManager, setInProgressTxId, setTotalStep, startNewTransaction, txQueueApi, wrappableToken] ); return ( @@ -261,12 +291,16 @@ const DepositConfirmContainer = forwardRef< }} totalProgress={totalStep} progress={currentStep} - onDownload={() => downloadNote(note)} amount={amount} - wrappingAmount={String(amount)} + wrappingAmount={amount} fungibleTokenSymbol={fungibleToken.view.symbol} - sourceChain={sourceChain} - destChain={destChain} + sourceTypedChainId={sourceTypedChainId} + destTypedChainId={destTypedChainId} + sourceAddress={activeAccount?.address ?? ''} + destAddress={note.note.targetIdentifyingData} + poolAddress={poolAddress} + poolExplorerUrl={poolExplorerUrl} + newBalance={newBalance} wrappableTokenSymbol={wrappableToken?.view.symbol} txStatusColor={ txStatus === 'completed' @@ -277,6 +311,17 @@ const DepositConfirmContainer = forwardRef< } txStatusMessage={txStatusMessage} onClose={onClose} + feesSection={ + , + }, + ]} + /> + } /> ); } diff --git a/apps/bridge-dapp/src/containers/DepositConfirmContainer/types.ts b/apps/bridge-dapp/src/containers/DepositConfirmContainer/types.ts index 0c7c1fb693..f690613148 100644 --- a/apps/bridge-dapp/src/containers/DepositConfirmContainer/types.ts +++ b/apps/bridge-dapp/src/containers/DepositConfirmContainer/types.ts @@ -1,4 +1,3 @@ -import { ChainGroup } from '@webb-tools/dapp-config/chains/chain-config.interface'; import { Note } from '@webb-tools/sdk-core/note'; import { PropsOf } from '@webb-tools/webb-ui-components/types'; @@ -16,18 +15,12 @@ export interface DepositConfirmContainerProps extends PropsOf<'div'> { /** * The source chain */ - sourceChain?: { - type: ChainGroup; - name: string; - }; + sourceTypedChainId?: number; /** * The destination chain */ - destChain?: { - type: ChainGroup; - name: string; - }; + destTypedChainId?: number; /** * The fungible token id diff --git a/apps/bridge-dapp/src/containers/TransferConfirmContainer/TransferConfirmContainer.tsx b/apps/bridge-dapp/src/containers/TransferConfirmContainer/TransferConfirmContainer.tsx index 065cc398d9..721e4ad85d 100644 --- a/apps/bridge-dapp/src/containers/TransferConfirmContainer/TransferConfirmContainer.tsx +++ b/apps/bridge-dapp/src/containers/TransferConfirmContainer/TransferConfirmContainer.tsx @@ -6,6 +6,9 @@ import { TransferTransactionPayloadType, } from '@webb-tools/abstract-api-provider'; import { useWebContext } from '@webb-tools/api-provider-environment'; +import { chainsConfig } from '@webb-tools/dapp-config/chains/chain-config'; +import { getExplorerURI } from '@webb-tools/api-provider-environment/transaction/utils'; +import { useBalancesFromNotes } from '@webb-tools/react-hooks/currency/useBalancesFromNotes'; import { LoggerService } from '@webb-tools/app-util'; import { ZERO_BIG_INT } from '@webb-tools/dapp-config'; import { WebbError, WebbErrorCodes } from '@webb-tools/dapp-types/WebbError'; @@ -21,10 +24,12 @@ import { TransferConfirm, getRoundedAmountString, } from '@webb-tools/webb-ui-components'; +import RelayerFeeDetails from '../../components/RelayerFeeDetails'; import { forwardRef, useMemo, useState, type ComponentProps } from 'react'; import type { Hash } from 'viem'; import { ContractFunctionRevertedError, formatEther } from 'viem'; import { useEnqueueSubmittedTx } from '../../hooks'; +import useTransferFeeCalculation from '../../hooks/useTransferFeeCalculation'; import useInProgressTxInfo from '../../hooks/useInProgressTxInfo'; import { captureSentryException, @@ -69,7 +74,9 @@ const TransferConfirmContainer = forwardRef< // State for tracking the status of the change note checkbox const [isChecked, setIsChecked] = useState(false); - const { activeApi, activeChain } = useWebContext(); + const { apiConfig, activeApi, activeChain, noteManager } = useWebContext(); + + const { balances } = useBalancesFromNotes(); const { cardTitle, @@ -85,11 +92,34 @@ const TransferConfirmContainer = forwardRef< onResetState ); + const srcTypedChainId = useMemo( + () => calculateTypedChainId(activeChain!.chainType, activeChain!.id), + [] + ); + const targetChainId = useMemo( () => calculateTypedChainId(destChain.chainType, destChain.id), [destChain] ); + const poolAddress = useMemo( + () => apiConfig.anchors[currency.id][targetChainId], + [apiConfig, currency.id, targetChainId] + ); + + const poolExplorerUrl = useMemo(() => { + const blockExplorerUrl = + chainsConfig[targetChainId]?.blockExplorers?.default.url; + + if (!blockExplorerUrl) return undefined; + return getExplorerURI( + blockExplorerUrl, + poolAddress, + 'address', + 'web3' + ).toString(); + }, [targetChainId, poolAddress]); + const { relayersState: { activeRelayer }, } = useRelayers({ @@ -118,6 +148,26 @@ const TransferConfirmContainer = forwardRef< targetTypedChainId: targetChainId, }); + const { + gasFeeInfo, + isLoading: isFeeLoading, + relayerFeeInfo, + totalFeeToken, + totalFeeWei, + } = useTransferFeeCalculation({ + activeRelayer, + recipientErrorMsg: undefined, + typedChainId: srcTypedChainId, + }); + + const newBalance = useMemo(() => { + const currentBalance = balances?.[currency.id]?.[srcTypedChainId]; + if (!currentBalance) return undefined; + const updatedBalance = Number(formatEther(currentBalance)) - amount; + if (updatedBalance < 0) return undefined; + return updatedBalance; + }, [balances, currency.id, srcTypedChainId, amount]); + const formattedFee = useMemo(() => { if (!feeInWei) { return undefined; @@ -140,15 +190,13 @@ const TransferConfirmContainer = forwardRef< progress={currentStep} amount={amount} changeAmount={changeAmount} - sourceChain={{ - name: activeChain?.name ?? '', - type: activeChain?.group ?? 'webb-dev', - }} - destChain={{ - name: destChain.name, - type: destChain.group ?? 'webb-dev', - }} note={changeNote?.serialize()} + sourceTypedChainId={srcTypedChainId} + destTypedChainId={targetChainId} + sourceAddress={noteManager?.getKeypair().toString() ?? ''} + destAddress={recipient} + poolAddress={poolAddress} + poolExplorerUrl={poolExplorerUrl} recipientTitleProps={{ info: , }} @@ -163,7 +211,6 @@ const TransferConfirmContainer = forwardRef< feeToken={feeToken} onClose={onClose} checkboxProps={{ - children: 'I have copied the change note', isChecked, onChange: () => setIsChecked((prev) => !prev), }} @@ -183,10 +230,28 @@ const TransferConfirmContainer = forwardRef< txStatusMessage={txStatusMessage} refundAmount={ typeof refundAmount === 'bigint' - ? formatEther(refundAmount) + ? Number(formatEther(refundAmount)) : undefined } refundToken={refundToken} + refundRecipient={refundRecipient} + newBalance={newBalance} + feesSection={ + + } /> ); } diff --git a/apps/bridge-dapp/src/containers/TxInfoContainer/TxInfoContainer.tsx b/apps/bridge-dapp/src/containers/TxInfoContainer/TxInfoContainer.tsx index e3a8c0ef98..bf78afa0eb 100644 --- a/apps/bridge-dapp/src/containers/TxInfoContainer/TxInfoContainer.tsx +++ b/apps/bridge-dapp/src/containers/TxInfoContainer/TxInfoContainer.tsx @@ -6,14 +6,16 @@ const TxInfoContainer = ({ hasRefund, refundAmount, refundToken, - remaining, - remainingToken, + newBalance, + newBalanceToken, + txType, }: { hasRefund?: boolean; refundAmount?: string; refundToken?: string; - remaining?: number; - remainingToken?: string; + newBalance?: number; + newBalanceToken?: string; + txType: 'deposit' | 'withdraw' | 'transfer'; }) => { return (
@@ -28,13 +30,13 @@ const TxInfoContainer = ({ )} } rightText={ - typeof remaining === 'number' - ? `${remaining.toString().slice(0, 10)} ${ - remainingToken ?? '' + typeof newBalance === 'number' + ? `${newBalance.toString().slice(0, 10)} ${ + newBalanceToken ?? '' }`.trim() : '--' } diff --git a/apps/bridge-dapp/src/containers/WithdrawConfirmContainer/WithdrawConfirmContainer.tsx b/apps/bridge-dapp/src/containers/WithdrawConfirmContainer/WithdrawConfirmContainer.tsx index 0101dfd9e6..b8b0e1a369 100644 --- a/apps/bridge-dapp/src/containers/WithdrawConfirmContainer/WithdrawConfirmContainer.tsx +++ b/apps/bridge-dapp/src/containers/WithdrawConfirmContainer/WithdrawConfirmContainer.tsx @@ -1,13 +1,15 @@ import { useWebContext } from '@webb-tools/api-provider-environment'; import { ZERO_BIG_INT, chainsPopulated } from '@webb-tools/dapp-config'; +import { chainsConfig } from '@webb-tools/dapp-config/chains/chain-config'; +import { getExplorerURI } from '@webb-tools/api-provider-environment/transaction/utils'; import { useRelayers, useVAnchor } from '@webb-tools/react-hooks'; +import { useBalancesFromNotes } from '@webb-tools/react-hooks/currency/useBalancesFromNotes'; import { ChainType, Note } from '@webb-tools/sdk-core'; import { WithdrawConfirm, getRoundedAmountString, } from '@webb-tools/webb-ui-components'; import { forwardRef, useCallback, useMemo, useState } from 'react'; - import { NewNotesTxResult, Transaction, @@ -23,6 +25,7 @@ import { } from 'viem'; import { useEnqueueSubmittedTx } from '../../hooks'; import useInProgressTxInfo from '../../hooks/useInProgressTxInfo'; +import useWithdrawFeeCalculation from '../../hooks/useWithdrawFeeCalculation'; import { captureSentryException, getErrorMessage, @@ -31,6 +34,7 @@ import { handleMutateNoteIndex, handleStoreNote, } from '../../utils'; +import RelayerFeeDetails from '../../components/RelayerFeeDetails'; import { WithdrawConfirmContainerProps } from './types'; const WithdrawConfirmContainer = forwardRef< @@ -52,10 +56,10 @@ const WithdrawConfirmContainer = forwardRef< onResetState, receivingInfo, recipient, - refundAmount, refundToken, sourceTypedChainId, targetTypedChainId, + refundAmount, unwrapCurrency: { value: unwrapCurrency } = {}, onClose, ...props @@ -74,6 +78,8 @@ const WithdrawConfirmContainer = forwardRef< const { activeApi, apiConfig, txQueue } = useWebContext(); + const { balances } = useBalancesFromNotes(); + const { api: txQueueApi } = txQueue; const { @@ -101,12 +107,51 @@ const WithdrawConfirmContainer = forwardRef< onResetState ); + const { + gasFeeInfo, + isLoading: isFeeLoading, + relayerFeeInfo, + totalFeeToken, + totalFeeWei, + } = useWithdrawFeeCalculation({ + activeRelayer, + recipientErrorMsg: undefined, + typedChainId: sourceTypedChainId, + }); + const avatarTheme = useMemo(() => { return chainsPopulated[targetTypedChainId].chainType === ChainType.EVM ? 'ethereum' : 'substrate'; }, [targetTypedChainId]); + const poolAddress = useMemo( + () => apiConfig.anchors[fungibleCurrency.id][targetTypedChainId], + [apiConfig, fungibleCurrency.id, targetTypedChainId] + ); + + const poolExplorerUrl = useMemo(() => { + const blockExplorerUrl = + chainsConfig[targetTypedChainId]?.blockExplorers?.default.url; + + if (!blockExplorerUrl) return undefined; + return getExplorerURI( + blockExplorerUrl, + poolAddress, + 'address', + 'web3' + ).toString(); + }, [targetTypedChainId, poolAddress]); + + const newBalance = useMemo(() => { + const currentBalance = + balances?.[fungibleCurrency.id]?.[sourceTypedChainId]; + if (!currentBalance) return undefined; + const updatedBalance = Number(formatEther(currentBalance)) - amount; + if (updatedBalance < 0) return undefined; + return updatedBalance; + }, [balances, fungibleCurrency.id, sourceTypedChainId, amount]); + // The main action onClick handler const handleExecuteWithdraw = useCallback( async () => { @@ -296,18 +341,6 @@ const WithdrawConfirmContainer = forwardRef< return `${feeInEthers} ${refundToken ?? ''}`; // Refund token here is the native token }, [activeRelayer, fee, fungibleCurrency.view.symbol, refundToken]); - const formattedRefund = useMemo(() => { - if (!refundAmount) { - return undefined; - } - - const refundInEthers = Number(formatEther(refundAmount)); - - return getRoundedAmountString(refundInEthers, 3, { - roundingFunction: Math.round, - }); - }, [refundAmount]); - const remainingAmount = useMemo(() => { const amountInEthers = Number(formatEther(amountAfterFee)); @@ -324,14 +357,6 @@ const WithdrawConfirmContainer = forwardRef< title={cardTitle} totalProgress={totalStep} progress={currentStep} - sourceChain={{ - name: chainsPopulated[sourceTypedChainId].name, - type: chainsPopulated[sourceTypedChainId].group ?? 'webb-dev', - }} - destChain={{ - name: chainsPopulated[targetTypedChainId].name, - type: chainsPopulated[targetTypedChainId].group ?? 'webb-dev', - }} actionBtnProps={{ isDisabled: inProgressTxId ? false : changeAmount ? !checked : false, children: inProgressTxId @@ -344,10 +369,13 @@ const WithdrawConfirmContainer = forwardRef< checkboxProps={{ isChecked: checked, isDisabled: Boolean(inProgressTxId), - children: 'I have copied the change note', onChange: () => setChecked((prev) => !prev), }} - refundAmount={isRefund ? formattedRefund : undefined} + refundAmount={ + isRefund && refundAmount + ? Number(formatEther(refundAmount)) + : undefined + } refundToken={isRefund ? refundToken : undefined} receivingInfo={receivingInfo} amount={amount} @@ -356,7 +384,17 @@ const WithdrawConfirmContainer = forwardRef< fee={formattedFee} note={changeNote?.serialize()} changeAmount={changeAmount} - recipientAddress={recipient} + sourceTypedChainId={sourceTypedChainId} + destTypedChainId={targetTypedChainId} + sourceAddress={ + availableNotes.length > 0 + ? availableNotes[0].note.sourceIdentifyingData + : '' + } + destAddress={recipient} + poolAddress={poolAddress} + poolExplorerUrl={poolExplorerUrl} + newBalance={newBalance} relayerAddress={activeRelayer?.beneficiary} relayerExternalUrl={activeRelayer?.endpoint} relayerAvatarTheme={avatarTheme} @@ -371,6 +409,22 @@ const WithdrawConfirmContainer = forwardRef< } txStatusMessage={txStatusMessage} onClose={onClose} + feesSection={ + + } /> ); } diff --git a/apps/bridge-dapp/src/hooks/index.ts b/apps/bridge-dapp/src/hooks/index.ts index 11fb420559..641a0ef72f 100644 --- a/apps/bridge-dapp/src/hooks/index.ts +++ b/apps/bridge-dapp/src/hooks/index.ts @@ -13,8 +13,10 @@ export * from './useMaxFeeInfo'; export { default as useNavigateWithPersistParams } from './useNavigateWithPersistParams'; export * from './useRelayerManager'; export { default as useRelayerWithRoute } from './useRelayerWithRoute'; +export { default as useTransferFeeCalculation } from './useTransferFeeCalculation'; export * from './useShieldedAssets'; export * from './useSpendNotes'; export { default as useStateWithRoute } from './useStateWithRoute'; export * from './useTryAnotherWalletWithView'; export { default as useTxTabFromRoute } from './useTxTabFromRoute'; +export { default as useWithdrawFeeCalculation } from './useWithdrawFeeCalculation'; diff --git a/apps/bridge-dapp/src/pages/Hubble/Bridge/Transfer/private/useFeeCalculation.ts b/apps/bridge-dapp/src/hooks/useTransferFeeCalculation.ts similarity index 94% rename from apps/bridge-dapp/src/pages/Hubble/Bridge/Transfer/private/useFeeCalculation.ts rename to apps/bridge-dapp/src/hooks/useTransferFeeCalculation.ts index 1c86603726..7f3dd5decc 100644 --- a/apps/bridge-dapp/src/pages/Hubble/Bridge/Transfer/private/useFeeCalculation.ts +++ b/apps/bridge-dapp/src/hooks/useTransferFeeCalculation.ts @@ -10,14 +10,11 @@ import { HAS_REFUND_KEY, RECIPIENT_KEY, REFUND_RECIPIENT_KEY, -} from '../../../../../constants'; -import useCurrenciesFromRoute from '../../../../../hooks/useCurrenciesFromRoute'; -import { - useMaxFeeInfo, - type MaxFeeInfoOption, -} from '../../../../../hooks/useMaxFeeInfo'; +} from '../constants'; +import useCurrenciesFromRoute from './useCurrenciesFromRoute'; +import { useMaxFeeInfo, type MaxFeeInfoOption } from './useMaxFeeInfo'; -export default function useFeeCalculation(args: { +export default function useTransferFeeCalculation(args: { typedChainId?: number | null; activeRelayer?: OptionalActiveRelayer; refundRecipientError?: string; diff --git a/apps/bridge-dapp/src/pages/Hubble/Bridge/Withdraw/private/useFeeCalculation.ts b/apps/bridge-dapp/src/hooks/useWithdrawFeeCalculation.ts similarity index 92% rename from apps/bridge-dapp/src/pages/Hubble/Bridge/Withdraw/private/useFeeCalculation.ts rename to apps/bridge-dapp/src/hooks/useWithdrawFeeCalculation.ts index 27ded6d90e..629fb34bdc 100644 --- a/apps/bridge-dapp/src/pages/Hubble/Bridge/Withdraw/private/useFeeCalculation.ts +++ b/apps/bridge-dapp/src/hooks/useWithdrawFeeCalculation.ts @@ -5,18 +5,11 @@ import numberToString from '@webb-tools/webb-ui-components/utils/numberToString' import { useEffect, useMemo } from 'react'; import { BooleanParam, StringParam, useQueryParams } from 'use-query-params'; import { formatEther, parseEther } from 'viem'; -import { - AMOUNT_KEY, - HAS_REFUND_KEY, - RECIPIENT_KEY, -} from '../../../../../constants'; -import useCurrenciesFromRoute from '../../../../../hooks/useCurrenciesFromRoute'; -import { - MaxFeeInfoOption, - useMaxFeeInfo, -} from '../../../../../hooks/useMaxFeeInfo'; - -export default function useFeeCalculation(args: { +import { AMOUNT_KEY, HAS_REFUND_KEY, RECIPIENT_KEY } from '../constants'; +import useCurrenciesFromRoute from './useCurrenciesFromRoute'; +import { MaxFeeInfoOption, useMaxFeeInfo } from './useMaxFeeInfo'; + +export default function useWithdrawFeeCalculation(args: { activeRelayer?: OptionalActiveRelayer; recipientErrorMsg?: string; typedChainId?: number | null; diff --git a/apps/bridge-dapp/src/pages/Hubble/Bridge/Deposit/index.tsx b/apps/bridge-dapp/src/pages/Hubble/Bridge/Deposit/index.tsx index 89989c93c3..23b8967a68 100644 --- a/apps/bridge-dapp/src/pages/Hubble/Bridge/Deposit/index.tsx +++ b/apps/bridge-dapp/src/pages/Hubble/Bridge/Deposit/index.tsx @@ -64,16 +64,16 @@ const Deposit = () => { ); const totalFungibleAmount = useMemo(() => { - if (typeof srcTypedChainId !== 'number') { + if (typeof destTypedChainId !== 'number') { return; } - if (fungibleCfg && shieldedBalances[fungibleCfg.id]?.[srcTypedChainId]) { + if (fungibleCfg && shieldedBalances[fungibleCfg.id]?.[destTypedChainId]) { return Number( - formatEther(shieldedBalances[fungibleCfg.id][srcTypedChainId]) + formatEther(shieldedBalances[fungibleCfg.id][destTypedChainId]) ); } - }, [shieldedBalances, fungibleCfg, srcTypedChainId]); + }, [shieldedBalances, fungibleCfg, destTypedChainId]); const lastPath = useMemo(() => pathname.split('/').pop(), [pathname]); if (lastPath && !BRIDGE_TABS.find((tab) => lastPath === tab)) { diff --git a/apps/bridge-dapp/src/pages/Hubble/Bridge/Deposit/private/useDepositButtonProps.tsx b/apps/bridge-dapp/src/pages/Hubble/Bridge/Deposit/private/useDepositButtonProps.tsx index c444ecce73..4d9186fb45 100644 --- a/apps/bridge-dapp/src/pages/Hubble/Bridge/Deposit/private/useDepositButtonProps.tsx +++ b/apps/bridge-dapp/src/pages/Hubble/Bridge/Deposit/private/useDepositButtonProps.tsx @@ -251,14 +251,8 @@ function useDepositButtonProps({ : undefined } amount={parseFloat(formatEther(amountBig))} - sourceChain={{ - name: srcChain.name, - type: srcChain.group, - }} - destChain={{ - name: destChain.name, - type: destChain.group, - }} + sourceTypedChainId={srcTypedId ?? undefined} + destTypedChainId={destTypedId ?? undefined} note={transactNote} onResetState={() => { setDepositConfirmComponent(null); diff --git a/apps/bridge-dapp/src/pages/Hubble/Bridge/Transfer/index.tsx b/apps/bridge-dapp/src/pages/Hubble/Bridge/Transfer/index.tsx index 3002a66666..9d9fcaa8ea 100644 --- a/apps/bridge-dapp/src/pages/Hubble/Bridge/Transfer/index.tsx +++ b/apps/bridge-dapp/src/pages/Hubble/Bridge/Transfer/index.tsx @@ -25,8 +25,8 @@ import { import { FC, useCallback, useMemo } from 'react'; import { Outlet, useLocation } from 'react-router'; import { formatEther, parseEther } from 'viem'; -import RelayerFeeDetails from '../../../../components/RelayerFeeDetails'; import SlideAnimation from '../../../../components/SlideAnimation'; +import RelayerFeeDetails from '../../../../components/RelayerFeeDetails'; import { BRIDGE_TABS, SELECT_DESTINATION_CHAIN_PATH, @@ -40,7 +40,7 @@ import useChainsFromRoute from '../../../../hooks/useChainsFromRoute'; import useCurrenciesFromRoute from '../../../../hooks/useCurrenciesFromRoute'; import useNavigateWithPersistParams from '../../../../hooks/useNavigateWithPersistParams'; import useRelayerWithRoute from '../../../../hooks/useRelayerWithRoute'; -import useFeeCalculation from './private/useFeeCalculation'; +import useTransferFeeCalculation from '../../../../hooks/useTransferFeeCalculation'; import useInputs from './private/useInputs'; import useTransferButtonProps from './private/useTransferButtonProps'; @@ -159,7 +159,7 @@ const Transfer = () => { resetMaxFeeInfo, totalFeeToken, totalFeeWei, - } = useFeeCalculation({ + } = useTransferFeeCalculation({ typedChainId: typedChainId, activeRelayer, recipientErrorMsg, @@ -363,8 +363,9 @@ const Transfer = () => { : undefined } refundToken={destChainCfg?.nativeCurrency.symbol} - remaining={remainingBalance} - remainingToken={fungibleCfg?.symbol} + newBalance={remainingBalance} + newBalanceToken={fungibleCfg?.symbol} + txType="transfer" />
diff --git a/apps/bridge-dapp/src/pages/Hubble/Bridge/Withdraw/index.tsx b/apps/bridge-dapp/src/pages/Hubble/Bridge/Withdraw/index.tsx index e9505c379d..ecd01961ad 100644 --- a/apps/bridge-dapp/src/pages/Hubble/Bridge/Withdraw/index.tsx +++ b/apps/bridge-dapp/src/pages/Hubble/Bridge/Withdraw/index.tsx @@ -21,11 +21,11 @@ import { useCopyable, useWebbUI, } from '@webb-tools/webb-ui-components'; +import RelayerFeeDetails from '../../../../components/RelayerFeeDetails'; import { useCallback, useMemo } from 'react'; import { Outlet, useLocation } from 'react-router-dom'; import { BooleanParam, useQueryParam } from 'use-query-params'; import { formatEther, parseEther } from 'viem'; -import RelayerFeeDetails from '../../../../components/RelayerFeeDetails'; import SlideAnimation from '../../../../components/SlideAnimation'; import { BRIDGE_TABS, @@ -45,7 +45,7 @@ import useChainsFromRoute from '../../../../hooks/useChainsFromRoute'; import useCurrenciesFromRoute from '../../../../hooks/useCurrenciesFromRoute'; import useNavigateWithPersistParams from '../../../../hooks/useNavigateWithPersistParams'; import useRelayerWithRoute from '../../../../hooks/useRelayerWithRoute'; -import useFeeCalculation from './private/useFeeCalculation'; +import useWithdrawFeeCalculation from '../../../../hooks/useWithdrawFeeCalculation'; import useInputs from './private/useInputs'; import useWithdrawButtonProps from './private/useWithdrawButtonProps'; @@ -150,7 +150,7 @@ const Withdraw = () => { resetMaxFeeInfo, totalFeeToken, totalFeeWei, - } = useFeeCalculation({ + } = useWithdrawFeeCalculation({ activeRelayer, recipientErrorMsg, typedChainId: srcTypedChainId, @@ -383,8 +383,9 @@ const Withdraw = () => { : undefined } refundToken={activeChain?.nativeCurrency.symbol} - remaining={remainingBalance} - remainingToken={fungibleCfg?.symbol} + newBalance={remainingBalance} + newBalanceToken={fungibleCfg?.symbol} + txType="withdraw" /> diff --git a/libs/dapp-config/src/utils/parseOnChainData.ts b/libs/dapp-config/src/utils/parseOnChainData.ts index 40b37665ac..970dd3e89c 100644 --- a/libs/dapp-config/src/utils/parseOnChainData.ts +++ b/libs/dapp-config/src/utils/parseOnChainData.ts @@ -53,14 +53,14 @@ function parseOnChainData(data: any) { ); // Iterate through the anchor metadata and add them to the config - anchorMetadata.forEach((metaddata) => { + anchorMetadata.forEach((metadata) => { const { address: anchorAddress, fungibleCurrency, isNativeAllowed, linkableAnchor, wrappableCurrencies, - } = metaddata; + } = metadata; // Add fungible currency to config const fungibleCurrencyConfig = addCurrencyToConfig( diff --git a/libs/icons/src/ChainIcon.tsx b/libs/icons/src/ChainIcon.tsx index d18c052b2a..bc87236b4b 100644 --- a/libs/icons/src/ChainIcon.tsx +++ b/libs/icons/src/ChainIcon.tsx @@ -47,6 +47,7 @@ export const ChainIcon: React.FC< className, width: sizeInNumber, height: sizeInNumber, + viewBox: '0 0 24 24', ...restProps, }; diff --git a/libs/icons/src/EyeClosedLine.tsx b/libs/icons/src/EyeClosedLine.tsx new file mode 100644 index 0000000000..241deb25bb --- /dev/null +++ b/libs/icons/src/EyeClosedLine.tsx @@ -0,0 +1,13 @@ +import { createIcon } from './create-icon'; +import { IconBase } from './types'; + +const EyeClosedLine = (props: IconBase) => { + return createIcon({ + ...props, + viewBox: '0 0 25 24', + d: 'M9.84268 18.7809L7.91083 18.2632L8.6983 15.3244C7.50919 14.8864 6.41661 14.2488 5.46116 13.4524L3.30783 15.6057L1.89362 14.1915L4.04695 12.0382C2.85581 10.6093 2.02014 8.87368 1.67578 6.96721L3.64386 6.60938C4.40289 10.8116 8.07931 13.9991 12.5002 13.9991C16.9211 13.9991 20.5976 10.8116 21.3566 6.60938L23.3247 6.96721C22.9803 8.87368 22.1446 10.6093 20.9535 12.0382L23.1068 14.1915L21.6926 15.6057L19.5393 13.4524C18.5838 14.2488 17.4912 14.8864 16.3021 15.3244L17.0896 18.2632L15.1578 18.7809L14.37 15.8408C13.7623 15.9449 13.1376 15.9991 12.5002 15.9991C11.8629 15.9991 11.2381 15.9449 10.6305 15.8408L9.84268 18.7809Z', + displayName: 'EyeClosedLine', + }); +}; + +export default EyeClosedLine; diff --git a/libs/icons/src/EyeLine.tsx b/libs/icons/src/EyeLine.tsx new file mode 100644 index 0000000000..6abae772d4 --- /dev/null +++ b/libs/icons/src/EyeLine.tsx @@ -0,0 +1,13 @@ +import { createIcon } from './create-icon'; +import { IconBase } from './types'; + +const EyeLine = (props: IconBase) => { + return createIcon({ + ...props, + viewBox: '0 0 25 24', + d: 'M12.5003 3C17.8924 3 22.3784 6.87976 23.3189 12C22.3784 17.1202 17.8924 21 12.5003 21C7.10812 21 2.62215 17.1202 1.68164 12C2.62215 6.87976 7.10812 3 12.5003 3ZM12.5003 19C16.7359 19 20.3603 16.052 21.2777 12C20.3603 7.94803 16.7359 5 12.5003 5C8.2646 5 4.64022 7.94803 3.72278 12C4.64022 16.052 8.2646 19 12.5003 19ZM12.5003 16.5C10.015 16.5 8.00026 14.4853 8.00026 12C8.00026 9.51472 10.015 7.5 12.5003 7.5C14.9855 7.5 17.0003 9.51472 17.0003 12C17.0003 14.4853 14.9855 16.5 12.5003 16.5ZM12.5003 14.5C13.881 14.5 15.0003 13.3807 15.0003 12C15.0003 10.6193 13.881 9.5 12.5003 9.5C11.1196 9.5 10.0003 10.6193 10.0003 12C10.0003 13.3807 11.1196 14.5 12.5003 14.5Z', + displayName: 'EyeLine', + }); +}; + +export default EyeLine; diff --git a/libs/icons/src/FileShieldLine.tsx b/libs/icons/src/FileShieldLine.tsx new file mode 100644 index 0000000000..d4a32cc3e3 --- /dev/null +++ b/libs/icons/src/FileShieldLine.tsx @@ -0,0 +1,13 @@ +import { createIcon } from './create-icon'; +import { IconBase } from './types'; + +const FileShieldLine = (props: IconBase) => { + return createIcon({ + ...props, + viewBox: '0 0 17 16', + d: 'M9.83333 5.9987V2.66536H3.83333V13.332H7.87087C8.0892 13.6101 8.35353 13.8552 8.65733 14.0552L9.584 14.6654H3.16227C2.79663 14.6654 2.5 14.3694 2.5 14.0042V1.99323C2.5 1.63557 2.79913 1.33203 3.16814 1.33203H10.4979L14.5 5.33203V5.9987H9.83333ZM8.5 7.33203H14.5V11.298C14.5 11.9585 14.1658 12.5752 13.6094 12.9416L11.5 14.3306L9.3906 12.9416C8.8342 12.5752 8.5 11.9585 8.5 11.298V7.33203ZM9.83333 11.298C9.83333 11.5088 9.94107 11.7076 10.1239 11.828L11.5 12.7342L12.8761 11.828C13.0589 11.7076 13.1667 11.5088 13.1667 11.298V8.66536H9.83333V11.298Z', + displayName: 'FileShieldLine', + }); +}; + +export default FileShieldLine; diff --git a/libs/icons/src/chains/athena.svg b/libs/icons/src/chains/athena.svg index e9b5f70a20..6c9aeca466 100644 --- a/libs/icons/src/chains/athena.svg +++ b/libs/icons/src/chains/athena.svg @@ -1,5 +1,11 @@ - - + + - + + - + + ( + ({ className: classNameProp, address, isNoteAccount = false }) => { + return ( + + {isNoteAccount ? ( + + ) : ( + + )} + + + {isHex(address) ? shortenHex(address, 2) : shortenString(address, 2)} + + + ); + } +); + +export default AddressChip; diff --git a/libs/webb-ui-components/src/components/AddressChip/index.ts b/libs/webb-ui-components/src/components/AddressChip/index.ts new file mode 100644 index 0000000000..2b8c55f7ba --- /dev/null +++ b/libs/webb-ui-components/src/components/AddressChip/index.ts @@ -0,0 +1,4 @@ +import AddressChip from './AddressChip'; + +export { default as FeeDetails } from './AddressChip'; +export default AddressChip; diff --git a/libs/webb-ui-components/src/components/AddressChip/types.ts b/libs/webb-ui-components/src/components/AddressChip/types.ts new file mode 100644 index 0000000000..479198beef --- /dev/null +++ b/libs/webb-ui-components/src/components/AddressChip/types.ts @@ -0,0 +1,5 @@ +export interface AddressChipProps { + address: string; + isNoteAccount?: boolean; + className?: string; +} diff --git a/libs/webb-ui-components/src/components/CopyWithTooltip/CopyWithTooltip.tsx b/libs/webb-ui-components/src/components/CopyWithTooltip/CopyWithTooltip.tsx index 9dc504fe35..3023c2f7f8 100644 --- a/libs/webb-ui-components/src/components/CopyWithTooltip/CopyWithTooltip.tsx +++ b/libs/webb-ui-components/src/components/CopyWithTooltip/CopyWithTooltip.tsx @@ -21,6 +21,8 @@ export const CopyWithTooltip: React.FC = ({ className, textToCopy, isButton = true, + iconSize = 'md', + iconClassName, }) => { const { copy, isCopied } = useCopyable(); @@ -32,6 +34,8 @@ export const CopyWithTooltip: React.FC = ({ }} isCopied={isCopied} isButton={isButton} + iconSize={iconSize} + iconClassName={iconClassName} /> ); }; @@ -47,7 +51,9 @@ const CopyWithTooltipUI: React.FC = ({ isCopied, onClick, className, + iconClassName, isButton, + iconSize, }) => { const [isTooltipOpen, setIsTooltipOpen] = useState(false); @@ -62,11 +68,17 @@ const CopyWithTooltipUI: React.FC = ({ > {isButton ? ( ) : ( - + )} diff --git a/libs/webb-ui-components/src/components/CopyWithTooltip/types.ts b/libs/webb-ui-components/src/components/CopyWithTooltip/types.ts index ad05707faf..0ce2106a3d 100644 --- a/libs/webb-ui-components/src/components/CopyWithTooltip/types.ts +++ b/libs/webb-ui-components/src/components/CopyWithTooltip/types.ts @@ -1,4 +1,5 @@ import { WebbComponentBase } from '../../types'; +import { IconSize } from '@webb-tools/icons/types'; /** * The `CopyWithTooltip` props @@ -13,20 +14,30 @@ export interface CopyWithTooltipProps extends WebbComponentBase { * Display the icon inside the button or just the icon */ isButton?: boolean; + + /** + * The icon size, possible values: `md` (16px), `lg` (24px), `xl` (48px) + * @default "md" + */ + iconSize?: IconSize; + + /** + * The icon class name + */ + iconClassName?: string; } /** * The internal UI component */ -export interface CopyWithTooltipUIProps { +export interface CopyWithTooltipUIProps + extends Pick< + CopyWithTooltipProps, + 'isButton' | 'iconSize' | 'iconClassName' + > { onClick: () => void; className?: string; isCopied: boolean; - - /** - * Display the icon inside the button or just the icon - */ - isButton?: boolean; } diff --git a/libs/webb-ui-components/src/components/IconWithTooltip/IconWithTooltip.tsx b/libs/webb-ui-components/src/components/IconWithTooltip/IconWithTooltip.tsx index 2e85d4a084..e259362855 100644 --- a/libs/webb-ui-components/src/components/IconWithTooltip/IconWithTooltip.tsx +++ b/libs/webb-ui-components/src/components/IconWithTooltip/IconWithTooltip.tsx @@ -1,4 +1,4 @@ -import { FC, useState } from 'react'; +import { type FC } from 'react'; import { twMerge } from 'tailwind-merge'; import { Typography } from '../../typography/Typography'; @@ -12,17 +12,9 @@ export const IconWithTooltip: FC = ({ overrideTooltipTriggerProps, overrideTooltipProps, }) => { - const [isTooltipOpen, setIsTooltipOpen] = useState(false); - return ( - setIsTooltipOpen(nextOpen)} - {...overrideTooltipProps} - > + setIsTooltipOpen(true)} - onMouseLeave={() => setIsTooltipOpen(false)} {...overrideTooltipTriggerProps} className={twMerge( 'cursor-auto', diff --git a/libs/webb-ui-components/src/components/TitleWithInfo/TitleWithInfo.tsx b/libs/webb-ui-components/src/components/TitleWithInfo/TitleWithInfo.tsx index d367040cda..9f3b9bc18b 100644 --- a/libs/webb-ui-components/src/components/TitleWithInfo/TitleWithInfo.tsx +++ b/libs/webb-ui-components/src/components/TitleWithInfo/TitleWithInfo.tsx @@ -52,9 +52,9 @@ export const TitleWithInfo = forwardRef( - + {typeof info === 'string' ? ( - + {info} ) : ( diff --git a/libs/webb-ui-components/src/components/TokenWithAmount/types.ts b/libs/webb-ui-components/src/components/TokenWithAmount/types.ts index cfd838fb7c..6d70249c6c 100644 --- a/libs/webb-ui-components/src/components/TokenWithAmount/types.ts +++ b/libs/webb-ui-components/src/components/TokenWithAmount/types.ts @@ -15,4 +15,9 @@ export interface TokenWithAmountProps extends PropsOf<'div'> { * The token 2 symbol to display (in token-pair case) */ token2Symbol?: string; + + /** + * The prefix to display before the amount + */ + prefix?: '+' | '-'; } diff --git a/libs/webb-ui-components/src/components/TokensRing/TokensRing.tsx b/libs/webb-ui-components/src/components/TokensRing/TokensRing.tsx deleted file mode 100644 index bd8804a01b..0000000000 --- a/libs/webb-ui-components/src/components/TokensRing/TokensRing.tsx +++ /dev/null @@ -1,599 +0,0 @@ -import { TokenIcon } from '@webb-tools/icons'; -import { Typography } from '../../typography'; -import cx from 'classnames'; -import { forwardRef } from 'react'; - -import { Chip } from '../Chip'; -import { TokensRingProps } from './types'; - -export const TokensRing = forwardRef( - ( - { - amount = 0, - className, - destChain, - destLabel = 'destination', - sourceChain, - sourceLabel = 'source', - tokenPairString, - ...props - }, - ref - ) => { - return ( -
-
- - - - - - {/** Dot */} - - {sourceChain === 'dot' && ( - - - - {sourceLabel} - - - - - - )} - {destChain === 'dot' && ( - - - - {destLabel} - - - - - - )} - - {/** Avax */} - - {sourceChain === 'avax' && ( - - - - {sourceLabel} - - - - - - )} - {destChain === 'avax' && ( - - - - {destLabel} - - - - - - )} - - {/** KSM */} - - {sourceChain === 'ksm' && ( - - - - {sourceLabel} - - - - - - )} - {destChain === 'ksm' && ( - - - - {destLabel} - - - - - - )} - - {/** Eth */} - - {sourceChain === 'eth' && ( - - - - {sourceLabel} - - - - - - )} - {destChain === 'eth' && ( - - - - {destLabel} - - - - - - )} - - {/** Matic */} - - {sourceChain === 'matic' && ( - - - - {sourceLabel} - - - - - - )} - {destChain === 'matic' && ( - - - - {destLabel} - - - - - - )} - - {/** Op */} - - {sourceChain === 'op' && ( - - - - {sourceLabel} - - - - - - )} - {destChain === 'op' && ( - - - - {destLabel} - - - - - - )} - - {/** Arbitrum */} - - {sourceChain === 'arbitrum' && ( - - - - {sourceLabel} - - - - - - )} - {destChain === 'arbitrum' && ( - - - - {destLabel} - - - - - - )} - - {/** One */} - - {sourceChain === 'one' && ( - - - - {sourceLabel} - - - - - - )} - {destChain === 'one' && ( - - - - {destLabel} - - - - - - )} - -
- - {amount.toString()} - - {tokenPairString && ( - - {tokenPairString} - - )} -
-
-
- ); - } -); diff --git a/libs/webb-ui-components/src/components/TokensRing/index.ts b/libs/webb-ui-components/src/components/TokensRing/index.ts deleted file mode 100644 index 31e3b3ae5b..0000000000 --- a/libs/webb-ui-components/src/components/TokensRing/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './TokensRing'; diff --git a/libs/webb-ui-components/src/components/TokensRing/types.ts b/libs/webb-ui-components/src/components/TokensRing/types.ts deleted file mode 100644 index d68e2349ba..0000000000 --- a/libs/webb-ui-components/src/components/TokensRing/types.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { PropsOf } from '../../types'; - -export type TokenRingValue = - | 'eth' - | 'dot' - | 'avax' - | 'ksm' - | 'one' - | 'arbitrum' - | 'op' - | 'matic'; - -export interface TokensRingProps extends PropsOf<'div'> { - /** - * The source (or from) chain - */ - sourceChain?: TokenRingValue; - - /** - * The destination (or to) chain - */ - destChain?: TokenRingValue; - - /** - * The transaction amount - */ - amount?: number | string; - - /** - * The token pair to display - */ - tokenPairString?: string; - - /** - * The source chain label - * @default "source" - */ - sourceLabel?: string; - - /** - * The destiantion chain label - * @default "destination" - */ - destLabel?: string; -} diff --git a/libs/webb-ui-components/src/components/TxConfirmationRing/TxConfirmationRing.tsx b/libs/webb-ui-components/src/components/TxConfirmationRing/TxConfirmationRing.tsx new file mode 100644 index 0000000000..129dc80896 --- /dev/null +++ b/libs/webb-ui-components/src/components/TxConfirmationRing/TxConfirmationRing.tsx @@ -0,0 +1,347 @@ +import { forwardRef } from 'react'; +import { ChainIcon, ExternalLinkLine } from '@webb-tools/icons'; +import { chainsConfig } from '@webb-tools/dapp-config/chains/chain-config'; +import cx from 'classnames'; +import { isHex } from 'viem'; + +import { TxConfirmationRingProps } from './types'; +import { Typography } from '../../typography'; +import AddressChip from '../AddressChip'; +import { useDarkMode } from '../../hooks/useDarkMode'; +import { shortenHex } from '../../utils'; + +const TxConfirmationRing = forwardRef( + ( + { + source, + dest, + poolName, + poolAddress, + poolExplorerUrl, + className, + ...props + }, + ref + ) => { + const [isDarkMode] = useDarkMode(); + + return ( +
+
+ {/* Source Chain Icon */} + + + {/* Destination Chain Icon */} + + + {/* Circle Inside (contain pool info) */} +
+
+ + {poolName} + + {isHex(poolAddress) && ( +
+ + {shortenHex(poolAddress)} + + {poolExplorerUrl && ( + + + + )} +
+ )} +
+
+ + + + {/* Source Address Chip */} + + + + + {/* Destination Address Chip */} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + } +); + +export default TxConfirmationRing; diff --git a/libs/webb-ui-components/src/components/TxConfirmationRing/index.ts b/libs/webb-ui-components/src/components/TxConfirmationRing/index.ts new file mode 100644 index 0000000000..ea63135076 --- /dev/null +++ b/libs/webb-ui-components/src/components/TxConfirmationRing/index.ts @@ -0,0 +1,4 @@ +import TxConfirmationRing from './TxConfirmationRing'; + +export { default as TxConfirmationRing } from './TxConfirmationRing'; +export default TxConfirmationRing; diff --git a/libs/webb-ui-components/src/components/TxConfirmationRing/types.ts b/libs/webb-ui-components/src/components/TxConfirmationRing/types.ts new file mode 100644 index 0000000000..916049f88a --- /dev/null +++ b/libs/webb-ui-components/src/components/TxConfirmationRing/types.ts @@ -0,0 +1,14 @@ +import { AddressChipProps } from '../AddressChip/types'; + +export interface TxConfirmationRingProps { + source: SourceOrDestination; + dest: SourceOrDestination; + poolName: string; + poolAddress: string; + poolExplorerUrl?: string; + className?: string; +} + +interface SourceOrDestination extends Omit { + typedChainId: number; +} diff --git a/libs/webb-ui-components/src/components/TxProgressor/TxProgressor.tsx b/libs/webb-ui-components/src/components/TxProgressor/TxProgressor.tsx index e5a7c382e1..9f6b83b88a 100644 --- a/libs/webb-ui-components/src/components/TxProgressor/TxProgressor.tsx +++ b/libs/webb-ui-components/src/components/TxProgressor/TxProgressor.tsx @@ -4,24 +4,21 @@ import { chainsConfig } from '@webb-tools/dapp-config/chains/chain-config'; import { ArrowRight, ExternalLinkLine, - ShieldKeyholeLineIcon, ShieldedAssetIcon, StatusIndicator, TokenIcon, - WalletLineIcon, } from '@webb-tools/icons'; -import cx from 'classnames'; import { forwardRef } from 'react'; import { twMerge } from 'tailwind-merge'; -import { isHex } from 'viem'; import useTimeAgo from '../../hooks/useTimeAgo'; import { PropsOf } from '../../types'; import { Typography } from '../../typography'; -import { shortenHex, shortenString } from '../../utils'; import { ChainChip } from '../ChainChip/ChainChip'; import { Chip, ChipProps } from '../Chip'; import SteppedProgress from '../Progress/SteppedProgress'; import { Button } from '../buttons'; +import { TitleWithInfo } from '../TitleWithInfo'; +import AddressChip from '../AddressChip'; import { TxInfo, TxProgressorBodyProps, @@ -89,6 +86,7 @@ const TxProgressorBodyItem: React.FC & TxInfo> = ({ typedChainId, isSource, walletAddress, + tooltipContent, ...props }) => { const chain = chainsConfig[typedChainId]; @@ -99,9 +97,17 @@ const TxProgressorBodyItem: React.FC & TxInfo> = ({ return (
-
+ + +
& TxInfo> = ({ /> {walletAddress && ( - - {accountType === 'note' ? ( - - ) : ( - - )} - - - {isHex(walletAddress) - ? shortenHex(walletAddress, 2) - : shortenString(walletAddress, 2)} - - + )}
-
+
& TxInfo> = ({ {tokenType === 'shielded' ? ( - + ) : ( - + )}
diff --git a/libs/webb-ui-components/src/components/TxProgressor/types.ts b/libs/webb-ui-components/src/components/TxProgressor/types.ts index feaada2b21..a361432102 100644 --- a/libs/webb-ui-components/src/components/TxProgressor/types.ts +++ b/libs/webb-ui-components/src/components/TxProgressor/types.ts @@ -45,6 +45,11 @@ export type TxInfo = { * The isSource flag to display the source info */ isSource?: boolean; + + /** + * The text for the tooltip + */ + tooltipContent?: string; }; export interface TxProgressorHeaderProps extends PropsOf<'div'> { diff --git a/libs/webb-ui-components/src/components/index.ts b/libs/webb-ui-components/src/components/index.ts index 30571d5d02..22e20a1fa8 100644 --- a/libs/webb-ui-components/src/components/index.ts +++ b/libs/webb-ui-components/src/components/index.ts @@ -1,4 +1,5 @@ export * from './Accordion'; +export { default as AddressChip } from './AddressChip'; export * from './Alert'; export * from './AmountMenu'; export * from './Avatar'; @@ -27,6 +28,7 @@ export * from './Dropdown'; export * from './DropdownMenu'; export * from './ErrorFallback'; export { default as FeeDetails } from './FeeDetails'; +export * from './FeeDetails'; export * from './FileUploads'; export * from './Filter'; export * from './Footer'; @@ -75,12 +77,12 @@ export * from './TokenPairIcons'; export * from './TokenSelector'; export { default as TokenSelector } from './TokenSelector'; export * from './TokenWithAmount'; -export * from './TokensRing'; export * from './Tooltip'; export * from './TransactionInputCard'; export { default as TransactionInputCard } from './TransactionInputCard'; export * from './TxProgressor'; export { default as TxProgressor } from './TxProgressor'; +export { default as TxConfirmationRing } from './TxConfirmationRing'; export * from './WalletConnectionCard'; export * from './WebsiteCommunity'; export * from './WebsiteFooter'; diff --git a/libs/webb-ui-components/src/containers/ConfirmationCard/AmountInfo.tsx b/libs/webb-ui-components/src/containers/ConfirmationCard/AmountInfo.tsx new file mode 100644 index 0000000000..2aa7c66605 --- /dev/null +++ b/libs/webb-ui-components/src/containers/ConfirmationCard/AmountInfo.tsx @@ -0,0 +1,61 @@ +import { type FC } from 'react'; +import { Typography } from '../../typography'; +import { IconWithTooltip } from '../../components/IconWithTooltip'; +import { + CornerDownRightLine, + FileShieldLine, + InformationLine, +} from '@webb-tools/icons'; + +interface AmountInfoProps { + label: string; + amount?: number | string; + tokenSymbol: string; + tooltipContent?: string; +} + +const AmountInfo: FC = ({ + label, + amount, + tokenSymbol, + tooltipContent, +}) => { + return ( +
+
+ + + + + {label} + + + {tooltipContent && ( + + } + content={ + + {tooltipContent} + + } + overrideTooltipBodyProps={{ className: 'max-w-[200px]' }} + /> + )} +
+ + {amount ?? '--'} {tokenSymbol} + +
+ ); +}; + +export default AmountInfo; diff --git a/libs/webb-ui-components/src/containers/ConfirmationCard/DepositConfirm.tsx b/libs/webb-ui-components/src/containers/ConfirmationCard/DepositConfirm.tsx index 1577b1fb09..35ac3a56e1 100644 --- a/libs/webb-ui-components/src/containers/ConfirmationCard/DepositConfirm.tsx +++ b/libs/webb-ui-components/src/containers/ConfirmationCard/DepositConfirm.tsx @@ -1,18 +1,18 @@ -import { ArrowRight, Close, Download } from '@webb-tools/icons'; -import { forwardRef, useMemo } from 'react'; +import { Close, FileShieldLine } from '@webb-tools/icons'; +import { forwardRef } from 'react'; import { twMerge } from 'tailwind-merge'; -import { InfoItem } from '../../components/BridgeInputs/InfoItem'; -import { ChainChip } from '../../components/ChainChip/ChainChip'; +import { TxProgressorBody } from '../../components/TxProgressor'; import { CheckBox } from '../../components/CheckBox/Checkbox'; import { Chip } from '../../components/Chip/Chip'; -import { CopyWithTooltip } from '../../components/CopyWithTooltip/CopyWithTooltip'; import SteppedProgress from '../../components/Progress/SteppedProgress'; import { TitleWithInfo } from '../../components/TitleWithInfo/TitleWithInfo'; -import { TokenWithAmount } from '../../components/TokenWithAmount/TokenWithAmount'; import Button from '../../components/buttons/Button'; import { Typography } from '../../typography'; -import { formatTokenAmount, getRoundedAmountString } from '../../utils'; -import { Section, WrapperSection } from './WrapperSection'; +import TxConfirmationRing from '../../components/TxConfirmationRing'; +import { formatTokenAmount } from './utils'; +import AmountInfo from './AmountInfo'; +import SpendNoteInput from './SpendNoteInput'; +import WrapperSection from './WrapperSection'; import { DepositConfirmProps } from './types'; export const DepositConfirm = forwardRef( @@ -23,45 +23,34 @@ export const DepositConfirm = forwardRef( wrappingAmount, checkboxProps, className, - destChain, fee, feeToken, note, onClose, - onDownload, txStatusMessage, txStatusColor = 'blue', progress = null, totalProgress, - sourceChain, + sourceAddress, + destAddress, + sourceTypedChainId, + destTypedChainId, title = 'Confirm Deposit', fungibleTokenSymbol, + poolAddress, + poolExplorerUrl, wrappableTokenSymbol, + newBalance, + feesSection, ...props }, ref ) => { - const depositingInfoStr = useMemo(() => { - let symbolStr = ''; - if (wrappableTokenSymbol) { - symbolStr += `${wrappableTokenSymbol.trim()}/`; - } - - symbolStr += fungibleTokenSymbol; - - const formatedAmount = - typeof amount === 'number' - ? getRoundedAmountString(amount, 3, { roundingFunction: Math.round }) - : amount ?? '0'; - - return `${formatedAmount} ${symbolStr}`; - }, [amount, fungibleTokenSymbol, wrappableTokenSymbol]); - return (
(
) : null} + {/** Wrapping info */} - {/** Wrapping info */} -
-
-
- - - -
+ + - + -
- - - -
-
-
+ {/** Spend Note info */} +
+
+ + +
- {/** New spend note */} -
-
-
- -
- - -
-
+ + + +
-
- - {note} - -
+ {/** Amount Details */} +
+ + +
- - {checkboxProps?.children ?? 'I have copied the spend note'} - -
- -
+ {/* Fees */} + {feesSection} - {/** Transaction Details */} -
-
- -
-
+ {/* Copy Spend Note Checkbox */} + + {checkboxProps?.children ?? + "I acknowledge that I've saved the spend note, essential for future transactions and fund access."} +
diff --git a/libs/webb-ui-components/src/containers/ConfirmationCard/RefundAmount.tsx b/libs/webb-ui-components/src/containers/ConfirmationCard/RefundAmount.tsx new file mode 100644 index 0000000000..eff4b66f02 --- /dev/null +++ b/libs/webb-ui-components/src/containers/ConfirmationCard/RefundAmount.tsx @@ -0,0 +1,86 @@ +import { type FC } from 'react'; +import { + CornerDownRightLine, + InformationLine, + WalletLineIcon, +} from '@webb-tools/icons'; + +import { CopyWithTooltip } from '../../components/CopyWithTooltip/CopyWithTooltip'; +import { IconWithTooltip } from '../../components/IconWithTooltip'; +import { Typography } from '../../typography'; + +interface RefundAmountProps { + amount?: number | string; + tokenSymbol: string; + refundAddress?: string; +} + +const RefundAmount: FC = ({ + amount, + tokenSymbol, + refundAddress, +}) => { + return ( +
+
+ + + + + Refund Amount + + + {refundAddress && ( + + } + content={ +
+ + Refund amount will be sent to: {refundAddress} + + + {/* Currently facing problem when hovering on the icon, the whole tooltip disappear */} + {/* data-state problem with radix-ui: https://github.com/radix-ui/primitives/discussions/560 */} + +
+ } + overrideTooltipBodyProps={{ + className: 'max-w-[200px]', + }} + /> + )} +
+
+ + {amount ?? '--'} {tokenSymbol} + + + + } + content={ + + Amount being refunded to recipient. + + } + overrideTooltipBodyProps={{ className: 'max-w-[200px]' }} + /> +
+
+ ); +}; + +export default RefundAmount; diff --git a/libs/webb-ui-components/src/containers/ConfirmationCard/SpendNoteInput.tsx b/libs/webb-ui-components/src/containers/ConfirmationCard/SpendNoteInput.tsx new file mode 100644 index 0000000000..36742376fc --- /dev/null +++ b/libs/webb-ui-components/src/containers/ConfirmationCard/SpendNoteInput.tsx @@ -0,0 +1,48 @@ +import { type FC, useState } from 'react'; +import { EyeLine, EyeClosedLine } from '@webb-tools/icons'; + +import { Typography } from '../../typography'; +import { CopyWithTooltip } from '../../components/CopyWithTooltip/CopyWithTooltip'; + +const SpendNoteInput: FC<{ note: string }> = ({ note }) => { + const [hidden, setHidden] = useState(true); + + return ( +
+ + {hidden ? '*'.repeat(40) : note} + +
+ {hidden ? ( + { + setHidden(false); + }} + className="!fill-mono-100" + /> + ) : ( + { + setHidden(true); + }} + className="!fill-mono-100" + /> + )} + +
+
+ ); +}; + +export default SpendNoteInput; diff --git a/libs/webb-ui-components/src/containers/ConfirmationCard/TransferConfirm.tsx b/libs/webb-ui-components/src/containers/ConfirmationCard/TransferConfirm.tsx index 91a954b2ce..4f072e88a8 100644 --- a/libs/webb-ui-components/src/containers/ConfirmationCard/TransferConfirm.tsx +++ b/libs/webb-ui-components/src/containers/ConfirmationCard/TransferConfirm.tsx @@ -1,41 +1,21 @@ -import { - ArrowRight, - Close, - Download, - ExternalLinkLine, -} from '@webb-tools/icons'; -import cx from 'classnames'; -import { forwardRef, useMemo } from 'react'; +import { Close, FileShieldLine } from '@webb-tools/icons'; +import { forwardRef } from 'react'; import { twMerge } from 'tailwind-merge'; -import { Avatar } from '../../components/Avatar/Avatar'; -import { InfoItem } from '../../components/BridgeInputs/InfoItem'; -import { ChainChip } from '../../components/ChainChip/ChainChip'; import { CheckBox } from '../../components/CheckBox/Checkbox'; import { Chip } from '../../components/Chip/Chip'; -import { CopyWithTooltip } from '../../components/CopyWithTooltip/CopyWithTooltip'; import SteppedProgress from '../../components/Progress/SteppedProgress'; import { TitleWithInfo } from '../../components/TitleWithInfo/TitleWithInfo'; -import { TokenWithAmount } from '../../components/TokenWithAmount/TokenWithAmount'; import Button from '../../components/buttons/Button'; import { Typography } from '../../typography/Typography'; -import { - formatTokenAmount, - getRoundedAmountString, - shortenString, -} from '../../utils'; -import { Section, WrapperSection } from './WrapperSection'; +import { TxProgressorBody } from '../../components/TxProgressor'; +import TxConfirmationRing from '../../components/TxConfirmationRing'; +import { formatTokenAmount } from './utils'; +import AmountInfo from './AmountInfo'; +import RefundAmount from './RefundAmount'; +import SpendNoteInput from './SpendNoteInput'; +import WrapperSection from './WrapperSection'; import { TransferConfirmProps } from './types'; -const defaultRecipientTitleProps: NonNullable< - TransferConfirmProps['recipientTitleProps'] -> = { - titleComponent: 'h6', - title: 'Recipient', - variant: 'utility', - titleClassName: 'text-mono-100 dark:text-mono-80', - className: 'text-mono-100 dark:text-mono-80', -}; - export const TransferConfirm = forwardRef( ( { @@ -44,12 +24,10 @@ export const TransferConfirm = forwardRef( changeAmount, checkboxProps, className, - destChain, fee, feeToken, note, onClose, - onDownload, progress, totalProgress, recipientTitleProps, @@ -57,74 +35,30 @@ export const TransferConfirm = forwardRef( relayerAddress, relayerExternalUrl, relayerAvatarTheme, - sourceChain, txStatusColor = 'blue', txStatusMessage, title = 'Confirm Transfer', + sourceAddress, + destAddress, + sourceTypedChainId, + destTypedChainId, + poolAddress, + poolExplorerUrl, fungibleTokenSymbol: token1Symbol, refundAmount, refundToken, + refundRecipient, + newBalance, + feesSection, ...props }, ref ) => { - const amountContent = useMemo(() => { - if (typeof amount !== 'number') { - return '--'; - } - - const formated = getRoundedAmountString(amount, 3, { - roundingFunction: Math.round, - }); - return `${formated} ${token1Symbol ?? ''}`.trim(); - }, [amount, token1Symbol]); - - const changeAmountContent = useMemo(() => { - if (typeof changeAmount !== 'number') { - return '--'; - } - - const formated = getRoundedAmountString(changeAmount, 3, { - roundingFunction: Math.round, - }); - return `${formated} ${token1Symbol ?? ''}`.trim(); - }, [changeAmount, token1Symbol]); - - const feeContent = useMemo(() => { - if (typeof fee === 'string') { - return `${fee} ${feeToken ?? ''}`; - } - - if (typeof fee === 'number') { - const formatedFee = getRoundedAmountString(fee, 3, { - roundingFunction: Math.round, - }); - return `${formatedFee} ${feeToken ?? ''}`.trim(); - } - - return '--'; - }, [fee, feeToken]); - - const refundContent = useMemo(() => { - if (typeof refundAmount === 'undefined') { - return; - } - - if (typeof refundAmount === 'string') { - return `${refundAmount.slice(0, 10)} ${refundToken ?? ''}`.trim(); - } - - const formated = getRoundedAmountString(refundAmount, 3, { - roundingFunction: Math.round, - }); - return `${formated} ${refundToken ?? ''}`.trim(); - }, [refundAmount, refundToken]); - return (
(
) : null} + {/* Transfer info */} - {/* Transfer info */} -
-
-
- - - -
- - - -
- - - -
-
-
- -
- {/** Relayer */} - {relayerAddress && ( -
-
- - -
- - - - {relayerAddress.toLowerCase().startsWith('0x') - ? shortenString(relayerAddress.substring(2), 5) - : shortenString(relayerAddress, 5)} - + + - - - -
-
-
- )} + {/* Ring */} + + + {/** Change Note info */} +
+
+ + +
- {/** Recipient public key */} - {recipientPublicKey && ( -
-
- + {typeof note === 'string' && ( + + + + )} +
-
- - {shortenString( - recipientPublicKey, - relayerAddress ? 7 : 19 - )} - - -
-
- - )} -
+ {/** Amount Details */} +
+ + +
- {/** New spend note */} - {note && ( -
-
-
- -
- - -
-
+ {/* Fees */} + {feesSection} -
- - {note} - -
+ {/* Refund */} + {refundAmount && ( + + )} - - {checkboxProps?.children ?? 'I have copied the spend note'} - -
-
+ {/* Copy Spend Note Checkbox */} + - - {/** Transaction Details */} -
-
- - {refundContent && ( - - )} - - -
-
+ > + {checkboxProps?.children ?? + "I acknowledge that I've saved the change note note (if applicable), essential for future transactions and fund access."} +
diff --git a/libs/webb-ui-components/src/containers/ConfirmationCard/WithdrawConfirm.tsx b/libs/webb-ui-components/src/containers/ConfirmationCard/WithdrawConfirm.tsx index 813e40c645..421bf32016 100644 --- a/libs/webb-ui-components/src/containers/ConfirmationCard/WithdrawConfirm.tsx +++ b/libs/webb-ui-components/src/containers/ConfirmationCard/WithdrawConfirm.tsx @@ -1,29 +1,20 @@ -import { - ArrowRight, - Close, - Download, - ExternalLinkLine, -} from '@webb-tools/icons'; -import cx from 'classnames'; -import { forwardRef, useMemo } from 'react'; +import { forwardRef } from 'react'; import { twMerge } from 'tailwind-merge'; -import { Avatar } from '../../components/Avatar/Avatar'; -import { InfoItem } from '../../components/BridgeInputs/InfoItem'; -import { ChainChip } from '../../components/ChainChip/ChainChip'; -import { CheckBox } from '../../components/CheckBox/Checkbox'; +import { Close, FileShieldLine } from '@webb-tools/icons'; + +import AmountInfo from './AmountInfo'; +import Button from '../../components/buttons/Button'; import { Chip } from '../../components/Chip/Chip'; -import { CopyWithTooltip } from '../../components/CopyWithTooltip/CopyWithTooltip'; +import { CheckBox } from '../../components/CheckBox/Checkbox'; import SteppedProgress from '../../components/Progress/SteppedProgress'; +import SpendNoteInput from './SpendNoteInput'; +import RefundAmount from './RefundAmount'; import { TitleWithInfo } from '../../components/TitleWithInfo/TitleWithInfo'; -import { TokenWithAmount } from '../../components/TokenWithAmount/TokenWithAmount'; -import Button from '../../components/buttons/Button'; import { Typography } from '../../typography'; -import { - formatTokenAmount, - getRoundedAmountString, - shortenString, -} from '../../utils'; -import { Section, WrapperSection } from './WrapperSection'; +import { TxProgressorBody } from '../../components/TxProgressor'; +import TxConfirmationRing from '../../components/TxConfirmationRing'; +import WrapperSection from './WrapperSection'; +import { formatTokenAmount } from './utils'; import { WithdrawConfirmationProps } from './types'; export const WithdrawConfirm = forwardRef< @@ -37,17 +28,15 @@ export const WithdrawConfirm = forwardRef< changeAmount, checkboxProps, className, - destChain, fee, feeInfo, fungibleTokenSymbol: token1Symbol, + wrappableTokenSymbol: token2Symbol, note, onClose, - onDownload, progress, totalProgress, receivingInfo, - recipientAddress, refundAmount, refundToken, relayerAddress, @@ -56,53 +45,24 @@ export const WithdrawConfirm = forwardRef< txStatusColor = 'blue', relayerExternalUrl, remainingAmount, - sourceChain, title = 'Confirm Withdrawal', - wrappableTokenSymbol: token2Symbol, + sourceAddress, + destAddress, + sourceTypedChainId, + destTypedChainId, + poolAddress, + poolExplorerUrl, + newBalance, + feesSection, ...props }, ref ) => { - const receivingContent = useMemo(() => { - if (!remainingAmount) { - return undefined; - } - - if (refundAmount) { - return `${remainingAmount} ${ - token2Symbol ?? token1Symbol - } + ${refundAmount} ${refundToken ?? ''}`; - } - - return `${remainingAmount} ${token2Symbol ?? token1Symbol}`.trim(); - }, [ - remainingAmount, - refundAmount, - refundToken, - token1Symbol, - token2Symbol, - ]); - - const changeAmountContent = useMemo(() => { - if (typeof changeAmount === 'undefined') { - return '--'; - } - - if (typeof changeAmount === 'string') { - return `${changeAmount.slice(0, 10)} ${token1Symbol}`.trim(); - } - - const formated = getRoundedAmountString(changeAmount, 3, { - roundingFunction: Math.round, - }); - return `${formated} ${token1Symbol ?? ''}`.trim(); - }, [changeAmount, token1Symbol]); - return (
) : null} + {/** Withdraw info */} - {/** Unwrapping\Withdrawing info */} -
-
-
- - - -
- -
- -
- -
- - - -
-
-
- -
- {/** Relayer */} - {relayerAddress && ( -
-
- - -
- - - - {relayerAddress.toLowerCase().startsWith('0x') - ? shortenString(relayerAddress.substring(2), 5) - : shortenString(relayerAddress, 5)} - + + - - - -
-
-
- )} + {/* Ring */} + + + {/** Change Note info */} +
+
+ + +
- {/** Recipient public key */} - {recipientAddress && ( -
-
- + {typeof note === 'string' && ( + + + + )} +
-
- - {shortenString( - recipientAddress, - relayerAddress ? 6 : 17 - )} - - -
-
- - )} -
+ {/** Amount Details */} +
+ + +
- {/** New spend note */} - {note && ( -
-
-
- -
- - -
-
+ {/* Fee Details */} + {feesSection} -
- - {note} - -
+ {/* Refund */} + {refundAmount && ( + + )} - - {checkboxProps?.children ?? 'I have copied the spend note'} - -
-
+ {/* Copy Spend Note Checkbox */} + - - {/** Transaction Details */} -
-
- - - -
-
+ > + {checkboxProps?.children ?? + "I acknowledge that I've saved the change note note (if applicable), essential for future transactions and fund access."} +
diff --git a/libs/webb-ui-components/src/containers/ConfirmationCard/WrapperSection.tsx b/libs/webb-ui-components/src/containers/ConfirmationCard/WrapperSection.tsx index 935409b372..7a998a76ea 100644 --- a/libs/webb-ui-components/src/containers/ConfirmationCard/WrapperSection.tsx +++ b/libs/webb-ui-components/src/containers/ConfirmationCard/WrapperSection.tsx @@ -9,7 +9,7 @@ export const WrapperSection = forwardRef>(
>( } ); -export const Section = forwardRef>( - ({ className, children, ...props }, ref) => { - return ( -
- {children} -
- ); - } -); +export default WrapperSection; diff --git a/libs/webb-ui-components/src/containers/ConfirmationCard/index.ts b/libs/webb-ui-components/src/containers/ConfirmationCard/index.ts index 9ad2362854..ca7d53ddbd 100644 --- a/libs/webb-ui-components/src/containers/ConfirmationCard/index.ts +++ b/libs/webb-ui-components/src/containers/ConfirmationCard/index.ts @@ -1,4 +1,3 @@ export * from './DepositConfirm'; export * from './TransferConfirm'; -export * from './utils'; export * from './WithdrawConfirm'; diff --git a/libs/webb-ui-components/src/containers/ConfirmationCard/types.ts b/libs/webb-ui-components/src/containers/ConfirmationCard/types.ts index 3788690cb4..fc9d7a4ac4 100644 --- a/libs/webb-ui-components/src/containers/ConfirmationCard/types.ts +++ b/libs/webb-ui-components/src/containers/ConfirmationCard/types.ts @@ -7,13 +7,22 @@ import type { ChipColors, TitleWithInfo, } from '../../components'; -import type { ChainGroup } from '@webb-tools/dapp-config'; export interface ConfirmationCardProps extends PropsOf<'div'> { + /** + * Source Address + */ + sourceAddress: string; + + /** + * Destination Address + */ + destAddress: string; + /** * The transaction amount */ - amount?: number | string; + amount: number; /** * The card title @@ -32,20 +41,14 @@ export interface ConfirmationCardProps extends PropsOf<'div'> { actionBtnProps?: ComponentProps; /** - * The source chain symbol + * The source typed chain id */ - sourceChain?: { - type: ChainGroup; - name: string; - }; + sourceTypedChainId: number; /** - * The destination chain symbol + * The destination typed chain id */ - destChain?: { - type: ChainGroup; - name: string; - }; + destTypedChainId: number; /** * The transaction progress @@ -72,12 +75,6 @@ export interface ConfirmationCardProps extends PropsOf<'div'> { */ note?: string | null; - /** - * Callback when user hits download button - * @returns void - */ - onDownload?: () => void; - /** * The checkbox props */ @@ -93,6 +90,26 @@ export interface ConfirmationCardProps extends PropsOf<'div'> { * @default 'blue' */ txStatusColor?: ChipColors; + + /** + * The address of the pool contract that the user is depositing to + */ + poolAddress: string; + + /** + * The block explorer of the pool contract + */ + poolExplorerUrl?: string; + + /** + * New balance if users decide to proceed with the transaction + */ + newBalance?: number; + + /** + * The component to display fee info of the tx + */ + feesSection: React.ReactNode; } export interface DepositConfirmProps extends ConfirmationCardProps { @@ -109,14 +126,14 @@ export interface DepositConfirmProps extends ConfirmationCardProps { /** * Due to wrapping fee, a wrapping amount would be larger than the bridged amount. */ - wrappingAmount?: string; + wrappingAmount?: number; } export interface WithdrawConfirmationProps extends ConfirmationCardProps { /** * The change amount */ - changeAmount?: number | string; + changeAmount?: number; /** * The first token symbol @@ -143,7 +160,7 @@ export interface WithdrawConfirmationProps extends ConfirmationCardProps { /** * The refund amount */ - refundAmount?: number | string; + refundAmount?: number; /** * The refund token symbol @@ -169,23 +186,18 @@ export interface WithdrawConfirmationProps extends ConfirmationCardProps { * The relayer external url */ relayerExternalUrl?: string; - - /** - * The recipient address - */ - recipientAddress: string; } export interface TransferConfirmProps extends ConfirmationCardProps { /** * The change amount */ - changeAmount?: number | string; + changeAmount?: number; /** * The first token symbol */ - fungibleTokenSymbol?: string; + fungibleTokenSymbol: string; /** * The relayer address @@ -216,10 +228,15 @@ export interface TransferConfirmProps extends ConfirmationCardProps { /** * The refund amount */ - refundAmount?: number | string; + refundAmount?: number; /** * The refund token symbol */ refundToken?: string; + + /** + * The address that will receive the refund + */ + refundRecipient?: string; } diff --git a/libs/webb-ui-components/src/containers/ConfirmationCard/utils.ts b/libs/webb-ui-components/src/containers/ConfirmationCard/utils.ts index 5d531ed4b6..887fbd2a4d 100644 --- a/libs/webb-ui-components/src/containers/ConfirmationCard/utils.ts +++ b/libs/webb-ui-components/src/containers/ConfirmationCard/utils.ts @@ -1,21 +1,7 @@ -import { TokenRingValue } from '../../components/TokensRing/types'; +import { getRoundedAmountString } from '../../utils'; -export const getTokenRingValue = ( - symbol: string -): TokenRingValue | undefined => { - const tkRingValues = [ - 'eth', - 'dot', - 'avax', - 'ksm', - 'one', - 'arbitrum', - 'op', - 'matic', - ]; - const isTokenRingValue = tkRingValues.includes(symbol.toLowerCase()); - - return isTokenRingValue - ? (symbol.toLowerCase() as TokenRingValue) - : undefined; +export const formatTokenAmount = (amount: number | undefined) => { + return getRoundedAmountString(amount, 5, { + roundingFunction: Math.round, + }); }; diff --git a/libs/webb-ui-components/src/stories/molecules/AddressChip.stories.jsx b/libs/webb-ui-components/src/stories/molecules/AddressChip.stories.jsx new file mode 100644 index 0000000000..e145249d17 --- /dev/null +++ b/libs/webb-ui-components/src/stories/molecules/AddressChip.stories.jsx @@ -0,0 +1,19 @@ +import { AddressChip } from '../../components'; + +export default { + title: 'Design System/Molecules/AddressChip', + component: AddressChip, +}; + +const Template = (args) => ; + +export const Default = Template.bind({}); +Default.args = { + address: '0x958aa9ddbd62f989dec2fd1468bf436aebeb8be6', +}; + +export const NoteAccount = Template.bind({}); +NoteAccount.args = { + address: '0x958aa9ddbd62f989dec2fd1468bf436aebeb8be6', + isNoteAccount: true, +}; diff --git a/libs/webb-ui-components/src/stories/molecules/TokenRing.stories.jsx b/libs/webb-ui-components/src/stories/molecules/TokenRing.stories.jsx deleted file mode 100644 index 344c0c7010..0000000000 --- a/libs/webb-ui-components/src/stories/molecules/TokenRing.stories.jsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; - -import { TokensRing } from '@webb-tools/webb-ui-components/components'; - -// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export -export default { - title: 'Design System/Molecules/TokensRing', - component: TokensRing, - // More on argTypes: https://storybook.js.org/docs/react/api/argtypes -}; - -// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args -const Template = (args) => ; - -export const Default = Template.bind({}); -// More on args: https://storybook.js.org/docs/react/writing-stories/args -Default.args = { - sourceLabel: 'depositing from', - destLabel: 'depositing to', - sourceChain: 'eth', - destChain: 'dot', - amount: '100', - tokenPairString: `ETH/DOT`, -}; diff --git a/libs/webb-ui-components/src/stories/molecules/TxConfirmationRing.stories.jsx b/libs/webb-ui-components/src/stories/molecules/TxConfirmationRing.stories.jsx new file mode 100644 index 0000000000..2dad5c920d --- /dev/null +++ b/libs/webb-ui-components/src/stories/molecules/TxConfirmationRing.stories.jsx @@ -0,0 +1,23 @@ +import { TxConfirmationRing } from '../../components'; + +export default { + title: 'Design System/Molecules/TxConfirmationRing', + component: TxConfirmationRing, +}; + +const Template = (args) => ; + +export const Default = Template.bind({}); +Default.args = { + poolName: 'webbETH', + poolAddress: '0x958aa9ddbd62f989dec2fd1468bf436aebeb8be6', + source: { + typedChainId: 1099511670889, + address: '0x958aa9ddbd62f989dec2fd1468bf436aebeb8be6', + }, + dest: { + typedChainId: 1099511707777, + address: '0x958aa9ddbd62f989dec2fd1468bf436aebeb8be6', + isNoteAccount: true, + }, +};