From 109794eec68d8eff21e391b7906729102067ce05 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 20 Jan 2025 16:03:22 -0300 Subject: [PATCH 01/85] feat: grouped wallet view WIP --- .../TransactionRequest/TransactionRequest.tsx | 23 +- .../TxOperationsSimple/TxOperationsSimple.tsx | 99 ++++ .../components/TxViewSimple/TxFeeSimple.tsx | 85 ++++ .../TxViewSimple/TxHeaderSimple.tsx | 107 +++++ .../TxViewSimple/TxOperationsSimple.tsx | 447 ++++++++++++++++++ .../components/TxViewSimple/TxViewSimple.tsx | 57 +++ .../components/TxViewSimple/index.tsx | 4 + .../components/TxViewSimpleWrapper.tsx | 60 +++ .../systems/Transaction/components/index.tsx | 2 + .../src/systems/Transaction/hooks/index.tsx | 1 + .../hooks/useSimplifiedTransaction.tsx | 28 ++ .../app/src/systems/Transaction/types.tsx | 65 +++ .../Transaction/utils/simplifyTransaction.ts | 201 ++++++++ 13 files changed, 1166 insertions(+), 13 deletions(-) create mode 100644 packages/app/src/systems/Transaction/components/TxOperationsSimple/TxOperationsSimple.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/index.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx create mode 100644 packages/app/src/systems/Transaction/hooks/useSimplifiedTransaction.tsx create mode 100644 packages/app/src/systems/Transaction/utils/simplifyTransaction.ts diff --git a/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx b/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx index 433923442c..f43023104d 100644 --- a/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx +++ b/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx @@ -5,7 +5,10 @@ import { useMemo } from 'react'; import { useAssets } from '~/systems/Asset'; import { Layout } from '~/systems/Core'; import { TopBarType } from '~/systems/Core/components/Layout/TopBar'; -import { TxContent, getGasLimitFromTxRequest } from '~/systems/Transaction'; +import { + TxViewSimpleWrapper, + getGasLimitFromTxRequest, +} from '~/systems/Transaction'; import { formatTip } from '~/systems/Transaction/components/TxFeeOptions/TxFeeOptions.utils'; import { useTransactionRequest } from '../../hooks/useTransactionRequest'; import { AutoSubmit } from './TransactionRequest.AutoSubmit'; @@ -28,8 +31,6 @@ export function TransactionRequest() { shouldShowTxExecuted, shouldShowActions, shouldDisableApproveBtn, - errors, - executedStatus, proposedTxRequest, } = txRequest; const { isLoading: isLoadingAssets } = useAssets(); @@ -63,7 +64,7 @@ export function TransactionRequest() { - + ); @@ -85,21 +86,17 @@ export function TransactionRequest() { {shouldShowTxSimulated && ( - )} {shouldShowTxExecuted && ( - ; + + return ( + + + {operations?.map((operation, index) => { + const isValidAddress = + isB256(operation.from) || isBech32(operation.from); + const fuelAddress = isValidAddress + ? Address.fromString(operation.from).toString() + : ''; + const name = + accounts?.find((a) => a.address === fuelAddress)?.name || 'unknown'; + + return ( + + + + + {name} + + + + + From: {operation.from} + + + To: {operation.to} + + + + ); + })} + + + ); +} + +TxOperationsSimple.Loader = function TxOperationsSimpleLoader() { + return ( + + + + + + + ); +}; + +const styles = { + operation: cssObj({ + padding: '$3', + }), + content: cssObj({ + display: 'flex', + flexDirection: 'column', + gap: '$3', + }), + info: cssObj({ + display: 'flex', + flexDirection: 'column', + gap: '$2', + }), + type: cssObj({ + display: 'flex', + alignItems: 'center', + gap: '$2', + }), + addresses: cssObj({ + display: 'flex', + flexDirection: 'column', + gap: '$1', + }), + amount: cssObj({ + alignSelf: 'flex-end', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx new file mode 100644 index 0000000000..e4845c4d1c --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx @@ -0,0 +1,85 @@ +import { cssObj } from '@fuel-ui/css'; +import { Box, Card, ContentLoader, Text } from '@fuel-ui/react'; +import { AssetsAmount } from '~/systems/Asset'; +import type { SimplifiedFee } from '../../types'; + +type TxFeeSimpleProps = { + fee: SimplifiedFee; + isLoading?: boolean; +}; + +export function TxFeeSimple({ fee, isLoading }: TxFeeSimpleProps) { + if (isLoading) return ; + + const { total, network, tip } = fee; + + return ( + + + + Network Fee + + + {tip && !tip.isZero() && ( + + Priority Fee + + + )} + + Total Fee + + + + + ); +} + +TxFeeSimple.Loader = function TxFeeSimpleLoader() { + return ( + + + + + + + + + ); +}; + +const styles = { + root: cssObj({ + padding: '$3', + }), + content: cssObj({ + display: 'flex', + flexDirection: 'column', + gap: '$3', + }), + row: cssObj({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + }), + total: cssObj({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + borderTop: '1px solid $gray4', + paddingTop: '$3', + marginTop: '$2', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx new file mode 100644 index 0000000000..3f689623ec --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx @@ -0,0 +1,107 @@ +import { cssObj } from '@fuel-ui/css'; +import { Box, Card, ContentLoader, Icon, Text } from '@fuel-ui/react'; +import type { TransactionStatus } from 'fuels'; + +type TxHeaderSimpleProps = { + status?: TransactionStatus; + origin?: { + name: string; + favicon?: string; + url?: string; + }; + isLoading?: boolean; +}; + +export function TxHeaderSimple({ isLoading }: TxHeaderSimpleProps) { + if (isLoading) return ; + + return ( + + Review Transaction + + + + Double-check the details of your transaction before submitting. + + + + ); +} + +TxHeaderSimple.Loader = function TxHeaderSimpleLoader() { + return ( + + + + + + ); +}; + +const styles = { + header: cssObj({ + display: 'flex', + flexDirection: 'column', + gap: '$2', + marginBottom: '$4', + backgroundColor: '$white', + borderBottom: '1px solid $border', + padding: '$4', + + '.fuel_Text': { + '&[as="h2"]': { + fontSize: '$xl', + fontWeight: '$normal', + margin: 0, + }, + }, + }), + warning: cssObj({ + display: 'flex', + alignItems: 'center', + gap: '$2', + fontSize: '$sm', + fontWeight: '$normal', + marginBottom: '$2', + + '.fuel_Text': { + '&[as="h1"]': { + fontSize: '$sm', + margin: 0, + fontWeight: '$normal', + }, + }, + + '.fuel_Icon': { + color: '$intentsWarning10', + }, + }), + root: cssObj({ + padding: '$3', + }), + content: cssObj({ + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + }), + origin: cssObj({ + display: 'flex', + alignItems: 'center', + gap: '$2', + }), + favicon: cssObj({ + width: '20px', + height: '20px', + borderRadius: '$md', + }), + icon: cssObj({ + width: '20px', + height: '20px', + color: '$gray9', + }), + status: cssObj({ + display: 'flex', + alignItems: 'center', + gap: '$2', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple.tsx new file mode 100644 index 0000000000..7a435ed38c --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple.tsx @@ -0,0 +1,447 @@ +import { cssObj } from '@fuel-ui/css'; +import { + Alert, + Avatar, + Box, + Card, + ContentLoader, + Drawer, + Icon, + IconButton, + Text, +} from '@fuel-ui/react'; +import { Address, isB256, isBech32 } from 'fuels'; +import { useEffect, useState } from 'react'; +import { useAccounts } from '~/systems/Account'; +import { AssetService } from '~/systems/Asset/services/assets'; +import { formatAmount, shortAddress } from '~/systems/Core'; +import { + type SimplifiedOperation, + type SwapMetadata, + TxCategory, +} from '../../types'; + +export type TxOperationsSimpleProps = { + operations?: SimplifiedOperation[]; + isLoading?: boolean; +}; + +type ContractCallMetadata = { + isContractCallGroup?: boolean; + operationCount?: number; + functionName?: string; + contractId?: string; +}; + +function isSwapMetadata( + metadata: ContractCallMetadata | SwapMetadata | undefined +): metadata is SwapMetadata { + return ( + metadata !== undefined && 'isSwap' in metadata && metadata.isSwap === true + ); +} + +export function TxOperationsSimple({ + operations, + isLoading, +}: TxOperationsSimpleProps) { + const [openDrawer, setOpenDrawer] = useState(null); + const [assetSymbols, setAssetSymbols] = useState>({}); + const { accounts } = useAccounts(); + + useEffect(() => { + async function loadAssetSymbols() { + const symbols: Record = {}; + if (operations) { + const allAssets = await AssetService.getAssets(); + for (const op of operations) { + if (op.assetId) { + const asset = allAssets.find((a) => + a.networks?.some( + (n) => n.type === 'fuel' && n.assetId === op.assetId + ) + ); + if (asset) { + symbols[op.assetId] = asset.symbol; + } + } + const metadata = op.metadata; + if (isSwapMetadata(metadata)) { + const asset = allAssets.find((a) => + a.networks?.some( + (n) => + n.type === 'fuel' && n.assetId === metadata.receiveAssetId + ) + ); + if (asset) { + symbols[metadata.receiveAssetId] = asset.symbol; + } + } + } + } + setAssetSymbols(symbols); + } + loadAssetSymbols(); + }, [operations]); + + if (isLoading) return ; + + return ( + + {operations?.map((operation, index) => { + const metadata = operation.metadata as ContractCallMetadata; + const isValidAddress = + isB256(operation.from) || isBech32(operation.from); + const fuelAddress = isValidAddress + ? Address.fromString(operation.from).toString() + : ''; + const name = + accounts?.find((a) => a.address === fuelAddress)?.name || 'unknown'; + const isGroup = + metadata?.isContractCallGroup && + metadata.operationCount && + metadata.operationCount > 1; + const key = + operation.groupId || + `${operation.type}-${operation.from}-${operation.to}-${index}`; + + return ( + + isGroup && setOpenDrawer(key)} + > + {isGroup && ( + + + This contract call occurs {metadata.operationCount} times + + + )} + + + + + + + + {name} + + {operation.type === TxCategory.CONTRACTCALL && ( + + + Contract + + + )} + + {shortAddress(fuelAddress)} + + navigator.clipboard.writeText(fuelAddress)} + /> + + + + {operation.type !== TxCategory.CONTRACTCALL && ( + + + + + + + + {isSwapMetadata(operation.metadata) + ? 'Swaps tokens' + : 'Sends token'} + + + + + + {formatAmount({ + amount: operation.amount || '0', + options: { units: 9 }, + })}{' '} + {assetSymbols[operation.assetId || ''] || + shortAddress(operation.assetId)} + + + {isSwapMetadata(operation.metadata) ? ( + + + + + + + + {accounts?.find( + (a) => a.address === operation.to + )?.name || 'unknown'} + + + + Contract + + + + {shortAddress(operation.to)} + + + navigator.clipboard.writeText( + operation.to + ) + } + /> + + + + + + + + + Sends token + + + + + {formatAmount({ + amount: + operation.metadata.receiveAmount, + options: { units: 9 }, + })}{' '} + {assetSymbols[ + operation.metadata.receiveAssetId + ] || + shortAddress( + operation.metadata.receiveAssetId + )} + + + + + + ) : ( + + + + + + + {accounts?.find( + (a) => a.address === operation.to + )?.name || 'unknown'} + + + {shortAddress(operation.to)} + + + navigator.clipboard.writeText(operation.to) + } + /> + + + )} + + + + + )} + + + + {isGroup && ( + setOpenDrawer(null)} + > + + + + Contract Calls + setOpenDrawer(null)} + /> + + + {metadata.functionName && ( + + Function: {metadata.functionName} + + )} + Contract: {operation.to} + {metadata.operationCount && ( + + Total Calls: {metadata.operationCount} + + )} + + + + + )} + + ); + })} + + ); +} + +TxOperationsSimple.Loader = function TxOperationsSimpleLoader() { + return ( + + + + + + + ); +}; + +const styles = { + operation: cssObj({ + padding: '$1', + }), + content: cssObj({ + display: 'flex', + flexDirection: 'column', + gap: '$3', + }), + info: cssObj({ + display: 'flex', + flexDirection: 'column', + gap: '$3', + cursor: 'pointer', + '&:hover': { + backgroundColor: '$gray2', + }, + padding: '$3', + }), + type: cssObj({ + display: 'flex', + alignItems: 'center', + gap: '$2', + }), + addresses: cssObj({ + display: 'flex', + flexDirection: 'column', + gap: '$1', + }), + amount: cssObj({ + alignSelf: 'flex-end', + }), + drawer: cssObj({ + display: 'grid', + height: '100%', + gridTemplateRows: '50px 1fr', + }), + drawerHeader: cssObj({ + px: '$4', + py: '$3', + alignItems: 'center', + justifyContent: 'space-between', + borderBottom: '1px solid $border', + fontWeight: '$normal', + }), + drawerContent: cssObj({ + padding: '$4', + display: 'flex', + flexDirection: 'column', + gap: '$3', + }), + tokenInfo: cssObj({ + display: 'flex', + flexDirection: 'column', + gap: '$2', + }), + alert: cssObj({ + padding: '0', + textAlign: 'center', + backgroundColor: '#E6F4FE', + color: '#0D74CE', + border: 'none', + borderRadius: '6px', + }), + badge: cssObj({ + backgroundColor: '$gray3', + padding: '$1 $2', + borderRadius: '$md', + marginLeft: '$2', + }), + timeline: cssObj({ + position: 'relative', + '&::before': { + content: '""', + position: 'absolute', + left: '12px', + top: 0, + bottom: 0, + width: '2px', + backgroundColor: '$gray4', + }, + }), + line: cssObj({ + display: 'flex', + alignItems: 'center', + gap: '$3', + }), + iconCol: cssObj({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '24px', + flexShrink: 0, + }), + contentCol: cssObj({ + display: 'flex', + flex: 1, + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx new file mode 100644 index 0000000000..7077fd0fc9 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx @@ -0,0 +1,57 @@ +import { cssObj } from '@fuel-ui/css'; +import { Box } from '@fuel-ui/react'; +import type { SimplifiedTransactionViewProps } from '../../types'; +import { TxFeeSimple } from './TxFeeSimple'; +import { TxHeaderSimple } from './TxHeaderSimple'; +import { TxOperationsSimple } from './TxOperationsSimple'; + +export function TxViewSimple({ + transaction, + showDetails = true, + isLoading, + footer, +}: SimplifiedTransactionViewProps) { + return ( + + + + + {showDetails && ( + + )} + {footer} + + + ); +} + +const styles = { + root: cssObj({ + display: 'flex', + flexDirection: 'column', + }), + content: cssObj({ + padding: '$1', + display: 'flex', + flexDirection: 'column', + gap: '$1', + }), +}; + +// Add a loader component for loading states +TxViewSimple.Loader = function TxViewSimpleLoader() { + return ( + + + + + + ); +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/index.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/index.tsx new file mode 100644 index 0000000000..8d1cc03cf5 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/index.tsx @@ -0,0 +1,4 @@ +export * from './TxOperationsSimple'; +export * from './TxViewSimple'; +export * from './TxHeaderSimple'; +export * from './TxFeeSimple'; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx b/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx new file mode 100644 index 0000000000..aa069036dd --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx @@ -0,0 +1,60 @@ +import type { TransactionRequest, TransactionSummary } from 'fuels'; +import { useSimplifiedTransaction } from '../hooks/useSimplifiedTransaction'; +import { TxViewSimple } from './TxViewSimple'; + +type TxViewSimpleWrapperProps = { + summary?: TransactionSummary; + request?: TransactionRequest; + showDetails?: boolean; + isLoading?: boolean; + footer?: React.ReactNode; +}; + +export function TxViewSimpleWrapper({ + summary, + request, + showDetails, + isLoading: externalLoading, + footer, +}: TxViewSimpleWrapperProps) { + console.log('TxViewSimpleWrapper props:', { + hasSummary: !!summary, + hasRequest: !!request, + externalLoading, + }); + + // If we have a summary but no explicit status, treat it as pending + const hasValidStatus = !!summary; + + // Only show loader for external loading or no summary + if (!hasValidStatus || externalLoading) { + console.log('Showing loader because:', { + missingStatus: !hasValidStatus, + externalLoading, + summaryExists: !!summary, + }); + return ; + } + + const { transaction, isReady } = useSimplifiedTransaction({ + summary, + request, + }); + + console.log('useSimplifiedTransaction result:', { + hasTransaction: !!transaction, + isReady, + transactionStatus: transaction?.status, + operationsCount: transaction?.operations?.length, + }); + + if (!isReady || !transaction) return null; + + return ( + + ); +} diff --git a/packages/app/src/systems/Transaction/components/index.tsx b/packages/app/src/systems/Transaction/components/index.tsx index 01403451a0..8ef934ecd2 100644 --- a/packages/app/src/systems/Transaction/components/index.tsx +++ b/packages/app/src/systems/Transaction/components/index.tsx @@ -7,3 +7,5 @@ export * from './TxOperation'; export * from './TxOperations'; export * from './TxRecipientCard'; export * from './TxStatusAlert'; +export * from './TxViewSimple'; +export * from './TxViewSimpleWrapper'; diff --git a/packages/app/src/systems/Transaction/hooks/index.tsx b/packages/app/src/systems/Transaction/hooks/index.tsx index 8fcd40d628..59a3e03c17 100644 --- a/packages/app/src/systems/Transaction/hooks/index.tsx +++ b/packages/app/src/systems/Transaction/hooks/index.tsx @@ -1,2 +1,3 @@ export * from './useTxResult'; export * from './useTransactionHistory'; +export * from './useSimplifiedTransaction'; diff --git a/packages/app/src/systems/Transaction/hooks/useSimplifiedTransaction.tsx b/packages/app/src/systems/Transaction/hooks/useSimplifiedTransaction.tsx new file mode 100644 index 0000000000..8e318cabc5 --- /dev/null +++ b/packages/app/src/systems/Transaction/hooks/useSimplifiedTransaction.tsx @@ -0,0 +1,28 @@ +import type { TransactionRequest, TransactionSummary } from 'fuels'; +import { useMemo } from 'react'; +import { useAccounts } from '~/systems/Account'; +import type { SimplifiedTransaction } from '../types'; +import { simplifyTransaction } from '../utils/simplifyTransaction'; + +type UseSimplifiedTransactionProps = { + summary?: TransactionSummary; + request?: TransactionRequest; +}; + +export function useSimplifiedTransaction({ + summary, + request, +}: UseSimplifiedTransactionProps) { + const { account } = useAccounts(); + + const transaction = useMemo(() => { + if (!summary) return undefined; + + return simplifyTransaction(summary, request, account?.address); + }, [summary, request, account?.address]); + + return { + transaction, + isReady: !!transaction, + }; +} diff --git a/packages/app/src/systems/Transaction/types.tsx b/packages/app/src/systems/Transaction/types.tsx index 2a250db96d..f08d801157 100644 --- a/packages/app/src/systems/Transaction/types.tsx +++ b/packages/app/src/systems/Transaction/types.tsx @@ -1,14 +1,20 @@ import type { AddressType, + BN, CallResult, CoinTransactionRequestInput, CoinTransactionRequestOutput, InputContract, OutputContract, OutputContractCreated, + TransactionRequest, TransactionRequestInput, TransactionRequestLike, + TransactionStatus, + TransactionSummary, } from 'fuels'; +import type { OperationFunctionCall } from 'fuels'; +import type { ReactNode } from 'react'; export enum TxCategory { SEND = 'send', @@ -44,3 +50,62 @@ export enum OperationDirection { from = 'From', unknown = 'Unknown', } + +export type ContractCallMetadata = { + contractId?: string; + functionName?: string; + functionData?: OperationFunctionCall; + amount?: BN; + assetId?: string; + isContractCallGroup?: boolean; + operationCount?: number; +}; + +export type SwapMetadata = { + isSwap: boolean; + receiveAmount: string; + receiveAssetId: string; +}; + +export type SimplifiedOperation = { + type: TxCategory; + from: string; + to: string; + amount?: BN; + assetId?: string; + isFromCurrentAccount?: boolean; + groupId?: string; + metadata?: ContractCallMetadata | SwapMetadata; +}; + +export type SimplifiedFee = { + total: BN; + network: BN; + tip?: BN; + gasUsed?: BN; + gasPrice?: BN; +}; + +export type SimplifiedTransaction = { + id?: string; + operations: SimplifiedOperation[]; + status: TransactionStatus; + timestamp?: Date; + fee: SimplifiedFee; + origin?: { + name: string; + favicon?: string; + url?: string; + }; + original: { + summary: TransactionSummary; + request?: TransactionRequest; + }; +}; + +export type SimplifiedTransactionViewProps = { + transaction: SimplifiedTransaction; + showDetails?: boolean; + isLoading?: boolean; + footer?: ReactNode; +}; diff --git a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts new file mode 100644 index 0000000000..4480012e2e --- /dev/null +++ b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts @@ -0,0 +1,201 @@ +import type { Operation, TransactionRequest, TransactionSummary } from 'fuels'; +import { + type OperationFunctionCall, + OperationName, + TransactionStatus, + bn, +} from 'fuels'; +import { + type ContractCallMetadata, + type SimplifiedFee, + type SimplifiedOperation, + type SimplifiedTransaction, + type SwapMetadata, + TxCategory, +} from '../types'; + +// Type for transaction request with optional origin properties +type TransactionRequestWithOrigin = TransactionRequest & { + origin?: string; + favIconUrl?: string; +}; + +function getOperationType(operation: Operation): TxCategory { + const { name } = operation; + + switch (name) { + case OperationName.transfer: + return TxCategory.SEND; + case OperationName.contractCall: + return TxCategory.CONTRACTCALL; + case OperationName.script: + return TxCategory.SCRIPT; + default: + return TxCategory.SEND; + } +} + +function transformOperation( + operation: Operation, + currentAccount?: string +): SimplifiedOperation { + const { name, from, to, assetsSent = [], calls = [] } = operation; + + // Determine if this operation is from the current account + const isFromCurrentAccount = currentAccount + ? from?.address === currentAccount + : false; + + // For contract calls, use the contract information + if (name === OperationName.contractCall && calls.length > 0) { + const call = calls[0] as OperationFunctionCall; // Take first call for now, we'll group them later + return { + type: TxCategory.CONTRACTCALL, + from: from?.address || '', + to: to?.address || '', + isFromCurrentAccount, + metadata: { + contractId: to?.address, + functionName: call.functionName, + functionData: call, + amount: call.amount ? bn(call.amount) : undefined, + assetId: call.assetId, + }, + }; + } + + // For transfers, use the asset information + if (assetsSent.length > 0) { + const asset = assetsSent[0]; // Take first asset for now, we'll group them later + return { + type: TxCategory.SEND, + from: from?.address || '', + to: to?.address || '', + amount: asset.amount ? bn(asset.amount) : undefined, + assetId: asset.assetId, + isFromCurrentAccount, + }; + } + + // Default case + return { + type: getOperationType(operation), + from: from?.address || '', + to: to?.address || '', + isFromCurrentAccount, + }; +} + +export function transformOperations( + summary: TransactionSummary, + currentAccount?: string +): SimplifiedOperation[] { + if (!summary.operations) return []; + + return summary.operations.map((op) => transformOperation(op, currentAccount)); +} + +export function groupSimilarOperations( + operations: SimplifiedOperation[] +): SimplifiedOperation[] { + // Group by type and contract address (except for contract calls which group by type only) + const groups = operations.reduce( + (acc, op) => { + const key = + op.type === TxCategory.CONTRACTCALL + ? op.type // Group all contract calls together + : `${op.type}-${op.to}`; // Other operations grouped by type and destination + if (!acc[key]) { + acc[key] = []; + } + acc[key].push(op); + return acc; + }, + {} as Record + ); + + // Combine operations in each group + return Object.values(groups).map((group) => { + if (group.length === 1) return group[0]; + + // Combine similar operations + return { + ...group[0], + groupId: `group-${group[0].type}${group[0].type !== TxCategory.CONTRACTCALL ? `-${group[0].to}` : ''}`, + metadata: { + ...group[0].metadata, + operationCount: group.length, + // Sum amounts if they exist and are the same asset + totalAmount: group.every( + (op) => op.amount && op.assetId === group[0].assetId + ) + ? group.reduce((sum, op) => sum.add(op.amount!), group[0].amount!) + : undefined, + }, + }; + }); +} + +export function simplifyFee( + summary: TransactionSummary, + request?: TransactionRequest +): SimplifiedFee { + const tip = request?.tip || bn(0); + return { + total: summary.fee || bn(0), + network: (summary.fee || bn(0)).sub(tip), + tip, + gasUsed: summary.gasUsed || bn(0), + gasPrice: bn(0), // This will be calculated later when we have access to the provider + }; +} + +function deriveStatus(summary: TransactionSummary): TransactionStatus { + if (summary.isStatusSuccess) return TransactionStatus.success; + if (summary.isStatusFailure) return TransactionStatus.failure; + if (summary.isStatusPending) return TransactionStatus.submitted; + return TransactionStatus.submitted; // Default to submitted if no status is set +} + +export function simplifyTransaction( + summary: TransactionSummary, + request?: TransactionRequest, + currentAccount?: string +): SimplifiedTransaction { + // Transform operations + const operations = transformOperations(summary, currentAccount); + + // Group similar operations + const groupedOperations = groupSimilarOperations(operations); + + // Sort operations (current account's operations first) + const sortedOperations = groupedOperations.sort((a, b) => { + if (a.isFromCurrentAccount && !b.isFromCurrentAccount) return -1; + if (!a.isFromCurrentAccount && b.isFromCurrentAccount) return 1; + return 0; + }); + + // Get origin info from the request context if available + const requestWithOrigin = request as TransactionRequestWithOrigin; + const origin = requestWithOrigin?.origin; + const favicon = requestWithOrigin?.favIconUrl; + + return { + id: summary.id, + operations: sortedOperations, + status: deriveStatus(summary), + timestamp: summary.time ? new Date(summary.time) : undefined, + fee: simplifyFee(summary, request), + origin: origin + ? { + name: origin, + favicon, + url: origin, + } + : undefined, + original: { + summary, + request, + }, + }; +} From 5c9557907ab2d9d12e8d19e19c321e0399cc55eb Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 20 Jan 2025 17:24:28 -0300 Subject: [PATCH 02/85] feat: separate into components --- .../TxViewSimple/TxOperationsSimple.tsx | 420 +----------------- .../TxOperationsSimple/TxOperationCard.tsx | 146 ++++++ .../TxOperationsSimple/TxOperationContent.tsx | 27 ++ .../TxOperationsSimple/TxOperationDrawer.tsx | 75 ++++ .../TxOperationsSimple/TxOperationHeader.tsx | 76 ++++ .../TxOperationsSimple/TxOperationSend.tsx | 58 +++ .../TxOperationsSimple/TxOperationSwap.tsx | 101 +++++ .../TxViewSimple/TxOperationsSimple/index.ts | 8 + .../TxOperationsSimple/useAssetSymbols.ts | 45 ++ .../TxViewSimple/TxOperationsSimple/utils.ts | 15 + .../Transaction/utils/simplifyTransaction.ts | 44 +- 11 files changed, 606 insertions(+), 409 deletions(-) create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationCard.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationContent.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationDrawer.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationHeader.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSend.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSwap.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/index.ts create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/useAssetSymbols.ts create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/utils.ts diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple.tsx index 7a435ed38c..ea2ca95384 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple.tsx @@ -1,336 +1,31 @@ import { cssObj } from '@fuel-ui/css'; -import { - Alert, - Avatar, - Box, - Card, - ContentLoader, - Drawer, - Icon, - IconButton, - Text, -} from '@fuel-ui/react'; -import { Address, isB256, isBech32 } from 'fuels'; -import { useEffect, useState } from 'react'; -import { useAccounts } from '~/systems/Account'; -import { AssetService } from '~/systems/Asset/services/assets'; -import { formatAmount, shortAddress } from '~/systems/Core'; -import { - type SimplifiedOperation, - type SwapMetadata, - TxCategory, -} from '../../types'; +import { Box, Card, ContentLoader } from '@fuel-ui/react'; +import type { SimplifiedOperation } from '../../types'; +import { TxOperationCard } from './TxOperationsSimple/TxOperationCard'; export type TxOperationsSimpleProps = { operations?: SimplifiedOperation[]; isLoading?: boolean; }; -type ContractCallMetadata = { - isContractCallGroup?: boolean; - operationCount?: number; - functionName?: string; - contractId?: string; -}; - -function isSwapMetadata( - metadata: ContractCallMetadata | SwapMetadata | undefined -): metadata is SwapMetadata { - return ( - metadata !== undefined && 'isSwap' in metadata && metadata.isSwap === true - ); -} - export function TxOperationsSimple({ operations, isLoading, }: TxOperationsSimpleProps) { - const [openDrawer, setOpenDrawer] = useState(null); - const [assetSymbols, setAssetSymbols] = useState>({}); - const { accounts } = useAccounts(); - - useEffect(() => { - async function loadAssetSymbols() { - const symbols: Record = {}; - if (operations) { - const allAssets = await AssetService.getAssets(); - for (const op of operations) { - if (op.assetId) { - const asset = allAssets.find((a) => - a.networks?.some( - (n) => n.type === 'fuel' && n.assetId === op.assetId - ) - ); - if (asset) { - symbols[op.assetId] = asset.symbol; - } - } - const metadata = op.metadata; - if (isSwapMetadata(metadata)) { - const asset = allAssets.find((a) => - a.networks?.some( - (n) => - n.type === 'fuel' && n.assetId === metadata.receiveAssetId - ) - ); - if (asset) { - symbols[metadata.receiveAssetId] = asset.symbol; - } - } - } - } - setAssetSymbols(symbols); - } - loadAssetSymbols(); - }, [operations]); - if (isLoading) return ; return ( - {operations?.map((operation, index) => { - const metadata = operation.metadata as ContractCallMetadata; - const isValidAddress = - isB256(operation.from) || isBech32(operation.from); - const fuelAddress = isValidAddress - ? Address.fromString(operation.from).toString() - : ''; - const name = - accounts?.find((a) => a.address === fuelAddress)?.name || 'unknown'; - const isGroup = - metadata?.isContractCallGroup && - metadata.operationCount && - metadata.operationCount > 1; - const key = - operation.groupId || - `${operation.type}-${operation.from}-${operation.to}-${index}`; - - return ( - - isGroup && setOpenDrawer(key)} - > - {isGroup && ( - - - This contract call occurs {metadata.operationCount} times - - - )} - - - - - - - - {name} - - {operation.type === TxCategory.CONTRACTCALL && ( - - - Contract - - - )} - - {shortAddress(fuelAddress)} - - navigator.clipboard.writeText(fuelAddress)} - /> - - - - {operation.type !== TxCategory.CONTRACTCALL && ( - - - - - - - - {isSwapMetadata(operation.metadata) - ? 'Swaps tokens' - : 'Sends token'} - - - - - - {formatAmount({ - amount: operation.amount || '0', - options: { units: 9 }, - })}{' '} - {assetSymbols[operation.assetId || ''] || - shortAddress(operation.assetId)} - - - {isSwapMetadata(operation.metadata) ? ( - - - - - - - - {accounts?.find( - (a) => a.address === operation.to - )?.name || 'unknown'} - - - - Contract - - - - {shortAddress(operation.to)} - - - navigator.clipboard.writeText( - operation.to - ) - } - /> - - - - - - - - - Sends token - - - - - {formatAmount({ - amount: - operation.metadata.receiveAmount, - options: { units: 9 }, - })}{' '} - {assetSymbols[ - operation.metadata.receiveAssetId - ] || - shortAddress( - operation.metadata.receiveAssetId - )} - - - - - - ) : ( - - - - - - - {accounts?.find( - (a) => a.address === operation.to - )?.name || 'unknown'} - - - {shortAddress(operation.to)} - - - navigator.clipboard.writeText(operation.to) - } - /> - - - )} - - - - - )} - - - - {isGroup && ( - setOpenDrawer(null)} - > - - - - Contract Calls - setOpenDrawer(null)} - /> - - - {metadata.functionName && ( - - Function: {metadata.functionName} - - )} - Contract: {operation.to} - {metadata.operationCount && ( - - Total Calls: {metadata.operationCount} - - )} - - - - - )} - - ); - })} + {operations?.map((operation, index) => ( + + ))} ); } @@ -355,93 +50,4 @@ const styles = { flexDirection: 'column', gap: '$3', }), - info: cssObj({ - display: 'flex', - flexDirection: 'column', - gap: '$3', - cursor: 'pointer', - '&:hover': { - backgroundColor: '$gray2', - }, - padding: '$3', - }), - type: cssObj({ - display: 'flex', - alignItems: 'center', - gap: '$2', - }), - addresses: cssObj({ - display: 'flex', - flexDirection: 'column', - gap: '$1', - }), - amount: cssObj({ - alignSelf: 'flex-end', - }), - drawer: cssObj({ - display: 'grid', - height: '100%', - gridTemplateRows: '50px 1fr', - }), - drawerHeader: cssObj({ - px: '$4', - py: '$3', - alignItems: 'center', - justifyContent: 'space-between', - borderBottom: '1px solid $border', - fontWeight: '$normal', - }), - drawerContent: cssObj({ - padding: '$4', - display: 'flex', - flexDirection: 'column', - gap: '$3', - }), - tokenInfo: cssObj({ - display: 'flex', - flexDirection: 'column', - gap: '$2', - }), - alert: cssObj({ - padding: '0', - textAlign: 'center', - backgroundColor: '#E6F4FE', - color: '#0D74CE', - border: 'none', - borderRadius: '6px', - }), - badge: cssObj({ - backgroundColor: '$gray3', - padding: '$1 $2', - borderRadius: '$md', - marginLeft: '$2', - }), - timeline: cssObj({ - position: 'relative', - '&::before': { - content: '""', - position: 'absolute', - left: '12px', - top: 0, - bottom: 0, - width: '2px', - backgroundColor: '$gray4', - }, - }), - line: cssObj({ - display: 'flex', - alignItems: 'center', - gap: '$3', - }), - iconCol: cssObj({ - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - width: '24px', - flexShrink: 0, - }), - contentCol: cssObj({ - display: 'flex', - flex: 1, - }), }; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationCard.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationCard.tsx new file mode 100644 index 0000000000..9849a48b37 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationCard.tsx @@ -0,0 +1,146 @@ +import { cssObj } from '@fuel-ui/css'; +import { + Alert, + Box, + Card, + Drawer, + Icon, + IconButton, + Text, +} from '@fuel-ui/react'; +import { useState } from 'react'; +import type { SimplifiedOperation } from '../../../types'; +import { TxCategory } from '../../../types'; +import { TxOperationContent } from './TxOperationContent'; +import { TxOperationHeader } from './TxOperationHeader'; +import { isContractCallMetadata } from './utils'; + +export type TxOperationCardProps = { + operation: SimplifiedOperation; + index: number; +}; + +export function TxOperationCard({ operation, index }: TxOperationCardProps) { + const [openDrawer, setOpenDrawer] = useState(null); + const metadata = operation.metadata; + console.log('TxOperationCard:', { + operation, + metadata, + type: operation.type, + isContractCallMetadata: metadata ? isContractCallMetadata(metadata) : false, + operationCount: + metadata && isContractCallMetadata(metadata) + ? metadata.operationCount + : 0, + }); + + const isGroup = + isContractCallMetadata(metadata) && + metadata.operationCount && + metadata.operationCount > 1; + + console.log('isGroup:', isGroup); + const key = + operation.groupId || + `${operation.type}-${operation.from}-${operation.to}-${index}`; + + const isContractCall = operation.type === TxCategory.CONTRACTCALL; + const isClickable = isContractCall && isGroup; + + return ( + + isClickable && setOpenDrawer(key)}> + {isGroup && ( + + + This contract call occurs {metadata.operationCount} times + + + )} + + + {!isGroup && } + + + + {isGroup && ( + setOpenDrawer(null)} + > + + + + Contract Calls + setOpenDrawer(null)} + /> + + + {isContractCallMetadata(metadata) && metadata.functionName && ( + Function: {metadata.functionName} + )} + Contract: {operation.to} + {isContractCallMetadata(metadata) && + metadata.operationCount && ( + + Total Calls: {metadata.operationCount} + + )} + + + + + )} + + ); +} + +const styles = { + info: cssObj({ + display: 'flex', + flexDirection: 'column', + gap: '$3', + cursor: ({ isClickable }: { isClickable: boolean }) => + isClickable ? 'pointer' : 'default', + '&:hover': { + backgroundColor: ({ isClickable }: { isClickable: boolean }) => + isClickable ? '$gray2' : 'transparent', + }, + padding: '$3', + }), + alert: cssObj({ + padding: '0', + textAlign: 'center', + backgroundColor: '#E6F4FE', + color: '#0D74CE', + border: 'none', + borderRadius: '6px', + }), + drawer: cssObj({ + display: 'grid', + height: '100%', + gridTemplateRows: '50px 1fr', + }), + drawerHeader: cssObj({ + px: '$4', + py: '$3', + alignItems: 'center', + justifyContent: 'space-between', + borderBottom: '1px solid $border', + fontWeight: '$normal', + }), + drawerContent: cssObj({ + padding: '$4', + display: 'flex', + flexDirection: 'column', + gap: '$3', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationContent.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationContent.tsx new file mode 100644 index 0000000000..7a075fd67f --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationContent.tsx @@ -0,0 +1,27 @@ +import { Box } from '@fuel-ui/react'; +import type { SimplifiedOperation } from '../../../types'; +import { TxCategory } from '../../../types'; +import { TxOperationSend } from './TxOperationSend'; +import { TxOperationSwap } from './TxOperationSwap'; + +type TxOperationContentProps = { + operation: SimplifiedOperation; +}; + +export function TxOperationContent({ operation }: TxOperationContentProps) { + console.log('TxOperationContent:', { + operation, + type: operation.type, + metadata: operation.metadata, + }); + + // Don't render content for contract calls - this is handled by the card + if (operation.type === TxCategory.CONTRACTCALL) return null; + + return ( + + + + + ); +} diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationDrawer.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationDrawer.tsx new file mode 100644 index 0000000000..295550a9f7 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationDrawer.tsx @@ -0,0 +1,75 @@ +import { cssObj } from '@fuel-ui/css'; +import { Box, Drawer, IconButton, Text } from '@fuel-ui/react'; +import type { SimplifiedOperation } from '../../../types'; + +type TxOperationDrawerProps = { + operation: SimplifiedOperation; + isOpen: boolean; + onClose: () => void; +}; + +export function TxOperationDrawer({ + operation, + isOpen, + onClose, +}: TxOperationDrawerProps) { + return ( + + + + + Contract Calls + + + + {operation.metadata?.functionName && ( + + Function: {operation.metadata.functionName} + + )} + Contract: {operation.to} + {operation.metadata?.operationCount && ( + + Total Calls: {operation.metadata.operationCount} + + )} + + + + + ); +} + +const styles = { + drawer: cssObj({ + display: 'grid', + height: '100%', + gridTemplateRows: '50px 1fr', + }), + drawerHeader: cssObj({ + px: '$4', + py: '$3', + alignItems: 'center', + justifyContent: 'space-between', + borderBottom: '1px solid $border', + fontWeight: '$normal', + }), + drawerContent: cssObj({ + padding: '$4', + display: 'flex', + flexDirection: 'column', + gap: '$3', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationHeader.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationHeader.tsx new file mode 100644 index 0000000000..21ab31dd04 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationHeader.tsx @@ -0,0 +1,76 @@ +import { cssObj } from '@fuel-ui/css'; +import { Avatar, Box, IconButton, Text } from '@fuel-ui/react'; +import { Address, isB256, isBech32 } from 'fuels'; +import { useAccounts } from '~/systems/Account'; +import { shortAddress } from '~/systems/Core'; +import type { SimplifiedOperation } from '../../../types'; +import { TxCategory } from '../../../types'; + +type TxOperationHeaderProps = { + operation: SimplifiedOperation; +}; + +export function TxOperationHeader({ operation }: TxOperationHeaderProps) { + const { accounts } = useAccounts(); + const isValidAddress = isB256(operation.from) || isBech32(operation.from); + const fuelAddress = isValidAddress + ? Address.fromString(operation.from).toString() + : ''; + const name = + accounts?.find((a) => a.address === fuelAddress)?.name || 'unknown'; + + return ( + + + + + + + {name} + + {operation.type === TxCategory.CONTRACTCALL && ( + + + Contract + + + )} + + {shortAddress(fuelAddress)} + + navigator.clipboard.writeText(fuelAddress)} + /> + + + ); +} + +const styles = { + line: cssObj({ + display: 'flex', + alignItems: 'center', + gap: '$3', + }), + iconCol: cssObj({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '24px', + flexShrink: 0, + }), + contentCol: cssObj({ + display: 'flex', + flex: 1, + }), + badge: cssObj({ + backgroundColor: '$gray3', + padding: '$1 $2', + borderRadius: '$md', + marginLeft: '$2', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSend.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSend.tsx new file mode 100644 index 0000000000..6fac2558cd --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSend.tsx @@ -0,0 +1,58 @@ +import { cssObj } from '@fuel-ui/css'; +import { Box, Icon, Text } from '@fuel-ui/react'; +import { formatAmount, shortAddress } from '~/systems/Core'; +import type { SimplifiedOperation } from '../../../types'; +import { useAssetSymbols } from './useAssetSymbols'; + +type TxOperationSendProps = { + operation: SimplifiedOperation; +}; + +export function TxOperationSend({ operation }: TxOperationSendProps) { + const { assetSymbols } = useAssetSymbols([operation]); + + return ( + + + + + + + {formatAmount({ + amount: operation.amount || '0', + options: { units: 9 }, + })}{' '} + {assetSymbols[operation.assetId || ''] || + shortAddress(operation.assetId)} + + + + + + ); +} + +const styles = { + line: cssObj({ + display: 'flex', + alignItems: 'center', + gap: '$3', + }), + iconCol: cssObj({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '24px', + flexShrink: 0, + }), + contentCol: cssObj({ + display: 'flex', + flex: 1, + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSwap.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSwap.tsx new file mode 100644 index 0000000000..6cb2a54f66 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSwap.tsx @@ -0,0 +1,101 @@ +import { cssObj } from '@fuel-ui/css'; +import { Avatar, Box, Icon, IconButton, Text } from '@fuel-ui/react'; +import { useAccounts } from '~/systems/Account'; +import { formatAmount, shortAddress } from '~/systems/Core'; +import type { SimplifiedOperation } from '../../../types'; +import { useAssetSymbols } from './useAssetSymbols'; +import { isSwapMetadata } from './utils'; + +type TxOperationSwapProps = { + operation: SimplifiedOperation; +}; + +export function TxOperationSwap({ operation }: TxOperationSwapProps) { + const { accounts } = useAccounts(); + const { assetSymbols } = useAssetSymbols([operation]); + + if (!isSwapMetadata(operation.metadata)) return null; + + return ( + <> + + + + + + + {accounts?.find((a) => a.address === operation.to)?.name || + 'unknown'} + + + + Contract + + + + {shortAddress(operation.to)} + + navigator.clipboard.writeText(operation.to)} + /> + + + + + + + + + Sends token + + + + + {formatAmount({ + amount: operation.metadata.receiveAmount, + options: { units: 9 }, + })}{' '} + {assetSymbols[operation.metadata.receiveAssetId] || + shortAddress(operation.metadata.receiveAssetId)} + + + + + + ); +} + +const styles = { + line: cssObj({ + display: 'flex', + alignItems: 'center', + gap: '$3', + }), + iconCol: cssObj({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '24px', + flexShrink: 0, + }), + contentCol: cssObj({ + display: 'flex', + flex: 1, + }), + badge: cssObj({ + backgroundColor: '$gray3', + padding: '$1 $2', + borderRadius: '$md', + marginLeft: '$2', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/index.ts b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/index.ts new file mode 100644 index 0000000000..4041aaf0b8 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/index.ts @@ -0,0 +1,8 @@ +export * from './TxOperationCard'; +export * from './TxOperationContent'; +export * from './TxOperationDrawer'; +export * from './TxOperationHeader'; +export * from './TxOperationSend'; +export * from './TxOperationSwap'; +export * from './utils'; +export * from './useAssetSymbols'; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/useAssetSymbols.ts b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/useAssetSymbols.ts new file mode 100644 index 0000000000..eef35b8f73 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/useAssetSymbols.ts @@ -0,0 +1,45 @@ +import { useEffect, useState } from 'react'; +import { AssetService } from '~/systems/Asset/services/assets'; +import type { SimplifiedOperation } from '../../../types'; +import { isSwapMetadata } from './utils'; + +export function useAssetSymbols(operations: SimplifiedOperation[]) { + const [assetSymbols, setAssetSymbols] = useState>({}); + + useEffect(() => { + async function loadAssetSymbols() { + const symbols: Record = {}; + if (operations) { + const allAssets = await AssetService.getAssets(); + for (const op of operations) { + if (op.assetId) { + const asset = allAssets.find((a) => + a.networks?.some( + (n) => n.type === 'fuel' && n.assetId === op.assetId + ) + ); + if (asset) { + symbols[op.assetId] = asset.symbol; + } + } + const metadata = op.metadata; + if (isSwapMetadata(metadata)) { + const asset = allAssets.find((a) => + a.networks?.some( + (n) => + n.type === 'fuel' && n.assetId === metadata.receiveAssetId + ) + ); + if (asset) { + symbols[metadata.receiveAssetId] = asset.symbol; + } + } + } + } + setAssetSymbols(symbols); + } + loadAssetSymbols(); + }, [operations]); + + return { assetSymbols }; +} diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/utils.ts b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/utils.ts new file mode 100644 index 0000000000..b386978ca9 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/utils.ts @@ -0,0 +1,15 @@ +import type { ContractCallMetadata, SwapMetadata } from '../../../types'; + +export function isSwapMetadata( + metadata: ContractCallMetadata | SwapMetadata | undefined +): metadata is SwapMetadata { + return ( + metadata !== undefined && 'isSwap' in metadata && metadata.isSwap === true + ); +} + +export function isContractCallMetadata( + metadata: ContractCallMetadata | SwapMetadata | undefined +): metadata is ContractCallMetadata { + return metadata !== undefined && 'operationCount' in metadata; +} diff --git a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts index 4480012e2e..8eb84ee649 100644 --- a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts +++ b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts @@ -98,8 +98,48 @@ export function transformOperations( export function groupSimilarOperations( operations: SimplifiedOperation[] ): SimplifiedOperation[] { - // Group by type and contract address (except for contract calls which group by type only) - const groups = operations.reduce( + const result: SimplifiedOperation[] = []; + const used = new Set(); + + // First pass: detect swaps + for (let i = 0; i < operations.length; i++) { + if (used.has(i)) continue; + + const current = operations[i]; + if (current.type === TxCategory.SEND && i + 1 < operations.length) { + const next = operations[i + 1]; + // Check if this is a swap: + // 1. Both operations are sends + // 2. The sender of the first is the receiver of the second + // 3. The receiver of the first is the sender of the second + if ( + next.type === TxCategory.SEND && + current.from === next.to && + current.to === next.from + ) { + // Combine the two operations into one swap + result.push({ + ...current, + metadata: { + isSwap: true, + receiveAmount: next.amount?.toString() || '0', + receiveAssetId: next.assetId || '', + } as SwapMetadata, + }); + used.add(i); + used.add(i + 1); + continue; + } + } + + if (!used.has(i)) { + result.push(current); + used.add(i); + } + } + + // Second pass: group similar non-swap operations + const groups = result.reduce( (acc, op) => { const key = op.type === TxCategory.CONTRACTCALL From bf478f60f39cb356914e4728b7f8a1c20de3626b Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 20 Jan 2025 20:39:45 -0300 Subject: [PATCH 03/85] fix(ecosystem): enhance error handling in fetchProjects method --- .../systems/Ecosystem/services/ecosystem.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/app/src/systems/Ecosystem/services/ecosystem.ts b/packages/app/src/systems/Ecosystem/services/ecosystem.ts index df384a9d5d..d6a2aa767f 100644 --- a/packages/app/src/systems/Ecosystem/services/ecosystem.ts +++ b/packages/app/src/systems/Ecosystem/services/ecosystem.ts @@ -4,13 +4,23 @@ import { ECOSYSTEM_PROJECTS_URL } from '~/config'; // biome-ignore lint/complexity/noStaticOnlyClass: export class EcosystemService { static async fetchProjects() { - const res = await fetch(ECOSYSTEM_PROJECTS_URL); + try { + const res = await fetch(ECOSYSTEM_PROJECTS_URL); + + if (!res.ok) { + console.error('Failed to fetch projects:', res.status, res.statusText); + const text = await res.text(); + console.error('Response text:', text); + throw new Error( + `Failed to fetch projects: ${res.status} ${res.statusText}` + ); + } - if (res.ok) { const data: EcosystemProject[] = await res.json(); return data; + } catch (err) { + console.error('Error fetching projects:', err); + throw err; } - - return []; } } From 2975d8a187b5b042e59ca527a8a3b91e913f1e01 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 20 Jan 2025 20:39:52 -0300 Subject: [PATCH 04/85] feat(Transaction): add totalAmount and operationCount to ContractCallMetadata and SwapMetadata types --- packages/app/src/systems/Transaction/types.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/app/src/systems/Transaction/types.tsx b/packages/app/src/systems/Transaction/types.tsx index f08d801157..0db494d9e3 100644 --- a/packages/app/src/systems/Transaction/types.tsx +++ b/packages/app/src/systems/Transaction/types.tsx @@ -59,12 +59,15 @@ export type ContractCallMetadata = { assetId?: string; isContractCallGroup?: boolean; operationCount?: number; + totalAmount?: BN; }; export type SwapMetadata = { isSwap: boolean; receiveAmount: string; receiveAssetId: string; + totalAmount?: BN; + operationCount?: number; }; export type SimplifiedOperation = { From a8f965dd8642fc1c99a91bc9f6199e142bd3befb Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 20 Jan 2025 20:40:02 -0300 Subject: [PATCH 05/85] refactor(TxViewSimpleWrapper): remove console logs and simplify loading logic --- .../components/TxViewSimpleWrapper.tsx | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx b/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx index aa069036dd..a71fc8687d 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx @@ -17,22 +17,10 @@ export function TxViewSimpleWrapper({ isLoading: externalLoading, footer, }: TxViewSimpleWrapperProps) { - console.log('TxViewSimpleWrapper props:', { - hasSummary: !!summary, - hasRequest: !!request, - externalLoading, - }); - // If we have a summary but no explicit status, treat it as pending const hasValidStatus = !!summary; - // Only show loader for external loading or no summary if (!hasValidStatus || externalLoading) { - console.log('Showing loader because:', { - missingStatus: !hasValidStatus, - externalLoading, - summaryExists: !!summary, - }); return ; } @@ -41,13 +29,6 @@ export function TxViewSimpleWrapper({ request, }); - console.log('useSimplifiedTransaction result:', { - hasTransaction: !!transaction, - isReady, - transactionStatus: transaction?.status, - operationsCount: transaction?.operations?.length, - }); - if (!isReady || !transaction) return null; return ( From 535df29450a341cf30f1a69ed9a6108e029ecd0d Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 20 Jan 2025 20:40:21 -0300 Subject: [PATCH 06/85] refactor(TxFeeSimple): streamline fee display logic and enhance asset handling --- .../components/TxViewSimple/TxFeeSimple.tsx | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx index e4845c4d1c..a43797d315 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx @@ -1,5 +1,6 @@ import { cssObj } from '@fuel-ui/css'; import { Box, Card, ContentLoader, Text } from '@fuel-ui/react'; +import type { AssetFuelAmount } from '@fuel-wallet/types'; import { AssetsAmount } from '~/systems/Asset'; import type { SimplifiedFee } from '../../types'; @@ -11,36 +12,25 @@ type TxFeeSimpleProps = { export function TxFeeSimple({ fee, isLoading }: TxFeeSimpleProps) { if (isLoading) return ; - const { total, network, tip } = fee; + const { network } = fee; + + const fuelAmount: AssetFuelAmount = { + type: 'fuel', + chainId: 0, + decimals: 9, + assetId: '', + name: 'Fuel', + symbol: 'ETH', + amount: network, + icon: '', + }; return ( Network Fee - - - {tip && !tip.isZero() && ( - - Priority Fee - - - )} - - Total Fee - + From 20171320b1f729b59c9b0397763cb6864830ff57 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 20 Jan 2025 20:40:31 -0300 Subject: [PATCH 07/85] refactor(TxOperationCard): remove console logs and simplify group detection logic --- .../TxOperationsSimple/TxOperationCard.tsx | 55 +------------------ 1 file changed, 3 insertions(+), 52 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationCard.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationCard.tsx index 9849a48b37..f44a347973 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationCard.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationCard.tsx @@ -21,25 +21,13 @@ export type TxOperationCardProps = { }; export function TxOperationCard({ operation, index }: TxOperationCardProps) { - const [openDrawer, setOpenDrawer] = useState(null); + const [_openDrawer, setOpenDrawer] = useState(null); const metadata = operation.metadata; - console.log('TxOperationCard:', { - operation, - metadata, - type: operation.type, - isContractCallMetadata: metadata ? isContractCallMetadata(metadata) : false, - operationCount: - metadata && isContractCallMetadata(metadata) - ? metadata.operationCount - : 0, - }); - const isGroup = isContractCallMetadata(metadata) && metadata.operationCount && - metadata.operationCount > 1; - - console.log('isGroup:', isGroup); + metadata.operationCount > 1 && + operation.groupId?.includes(operation.to); const key = operation.groupId || `${operation.type}-${operation.from}-${operation.to}-${index}`; @@ -62,43 +50,6 @@ export function TxOperationCard({ operation, index }: TxOperationCardProps) { {!isGroup && } - - {isGroup && ( - setOpenDrawer(null)} - > - - - - Contract Calls - setOpenDrawer(null)} - /> - - - {isContractCallMetadata(metadata) && metadata.functionName && ( - Function: {metadata.functionName} - )} - Contract: {operation.to} - {isContractCallMetadata(metadata) && - metadata.operationCount && ( - - Total Calls: {metadata.operationCount} - - )} - - - - - )} ); } From 6a06bdfaec45e94c93d289e4d40674ce07cce28c Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 20 Jan 2025 20:40:37 -0300 Subject: [PATCH 08/85] refactor(TxOperationContent): remove console logs to clean up code --- .../TxViewSimple/TxOperationsSimple/TxOperationContent.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationContent.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationContent.tsx index 7a075fd67f..59a12d7a73 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationContent.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationContent.tsx @@ -9,12 +9,6 @@ type TxOperationContentProps = { }; export function TxOperationContent({ operation }: TxOperationContentProps) { - console.log('TxOperationContent:', { - operation, - type: operation.type, - metadata: operation.metadata, - }); - // Don't render content for contract calls - this is handled by the card if (operation.type === TxCategory.CONTRACTCALL) return null; From 3deb36998cb17459661cb34cb717926af9ebec95 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 20 Jan 2025 20:40:48 -0300 Subject: [PATCH 09/85] feat(TxOperationHeader): enhance contract call display with project info --- .../TxOperationsSimple/TxOperationHeader.tsx | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationHeader.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationHeader.tsx index 21ab31dd04..caf93b470d 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationHeader.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationHeader.tsx @@ -5,6 +5,7 @@ import { useAccounts } from '~/systems/Account'; import { shortAddress } from '~/systems/Core'; import type { SimplifiedOperation } from '../../../types'; import { TxCategory } from '../../../types'; +import { useEcosystemProject } from './useEcosystemProject'; type TxOperationHeaderProps = { operation: SimplifiedOperation; @@ -19,16 +20,29 @@ export function TxOperationHeader({ operation }: TxOperationHeaderProps) { const name = accounts?.find((a) => a.address === fuelAddress)?.name || 'unknown'; + const isContract = operation.type === TxCategory.CONTRACTCALL; + const projectInfo = useEcosystemProject( + isContract ? operation.to : undefined + ); + return ( - + {projectInfo.image ? ( + + ) : ( + + )} - {name} + {isContract ? projectInfo.name || shortAddress(operation.to) : name} - {operation.type === TxCategory.CONTRACTCALL && ( + {isContract && ( Contract @@ -36,14 +50,18 @@ export function TxOperationHeader({ operation }: TxOperationHeaderProps) { )} - {shortAddress(fuelAddress)} + {shortAddress(isContract ? operation.to : fuelAddress)} navigator.clipboard.writeText(fuelAddress)} + onPress={() => + navigator.clipboard.writeText( + isContract ? operation.to : fuelAddress + ) + } /> From 9ddd6cfd4ac221bb332107302f3cf2ed1f51e839 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 20 Jan 2025 20:41:00 -0300 Subject: [PATCH 10/85] feat(TxOperationSend): enhance transaction display with asset amounts and contract call indication --- .../TxOperationsSimple/TxOperationSend.tsx | 50 ++++++++++++++----- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSend.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSend.tsx index 6fac2558cd..eee942da2e 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSend.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSend.tsx @@ -1,20 +1,28 @@ import { cssObj } from '@fuel-ui/css'; import { Box, Icon, Text } from '@fuel-ui/react'; -import { formatAmount, shortAddress } from '~/systems/Core'; +import { AssetsAmount } from '~/systems/Asset'; +import { shortAddress } from '~/systems/Core'; import type { SimplifiedOperation } from '../../../types'; -import { useAssetSymbols } from './useAssetSymbols'; +import { TxCategory } from '../../../types'; type TxOperationSendProps = { operation: SimplifiedOperation; }; export function TxOperationSend({ operation }: TxOperationSendProps) { - const { assetSymbols } = useAssetSymbols([operation]); + const amount = operation.metadata?.totalAmount || operation.amount || '0'; + const operationCount = operation.metadata?.operationCount; + const isContractCall = operation.type === TxCategory.CONTRACTCALL; return ( + {isContractCall && ( + + Calls contract (sending tokens) + + )} - - - {formatAmount({ - amount: operation.amount || '0', - options: { units: 9 }, - })}{' '} - {assetSymbols[operation.assetId || ''] || - shortAddress(operation.assetId)} - + + {operationCount && operationCount > 1 ? ( + + x{operationCount} + + ) : null} From 602c0cc85ccb2d0a65f14a89f1dfb4a2320c61cc Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 20 Jan 2025 20:41:06 -0300 Subject: [PATCH 11/85] feat(TxOperationSwap): enhance swap operation display with ecosystem project info and loading state --- .../TxOperationsSimple/TxOperationSwap.tsx | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSwap.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSwap.tsx index 6cb2a54f66..e182ebef79 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSwap.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSwap.tsx @@ -1,9 +1,9 @@ import { cssObj } from '@fuel-ui/css'; -import { Avatar, Box, Icon, IconButton, Text } from '@fuel-ui/react'; -import { useAccounts } from '~/systems/Account'; +import { Avatar, Box, Icon, IconButton, Spinner, Text } from '@fuel-ui/react'; import { formatAmount, shortAddress } from '~/systems/Core'; import type { SimplifiedOperation } from '../../../types'; import { useAssetSymbols } from './useAssetSymbols'; +import { useEcosystemProject } from './useEcosystemProject'; import { isSwapMetadata } from './utils'; type TxOperationSwapProps = { @@ -11,8 +11,12 @@ type TxOperationSwapProps = { }; export function TxOperationSwap({ operation }: TxOperationSwapProps) { - const { accounts } = useAccounts(); const { assetSymbols } = useAssetSymbols([operation]); + const { + name: projectName, + image: projectImage, + isLoading, + } = useEcosystemProject(operation.to); if (!isSwapMetadata(operation.metadata)) return null; @@ -20,12 +24,23 @@ export function TxOperationSwap({ operation }: TxOperationSwapProps) { <> - + {isLoading ? ( + + ) : projectImage ? ( + + ) : ( + + )} - {accounts?.find((a) => a.address === operation.to)?.name || - 'unknown'} + {isLoading + ? 'Loading...' + : projectName || shortAddress(operation.to)} From c28a430f7bb12e37b2bf24849ad73b172eea6af9 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 20 Jan 2025 20:41:12 -0300 Subject: [PATCH 12/85] feat(useEcosystemProject): implement custom hook for fetching and caching ecosystem project data --- .../TxOperationsSimple/useEcosystemProject.ts | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/useEcosystemProject.ts diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/useEcosystemProject.ts b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/useEcosystemProject.ts new file mode 100644 index 0000000000..e304d2906d --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/useEcosystemProject.ts @@ -0,0 +1,97 @@ +import type { EcosystemProject } from '@fuel-wallet/types'; +import { useEffect, useRef, useState } from 'react'; +import { EcosystemService } from '~/systems/Ecosystem/services/ecosystem'; +import { getProjectImage } from '~/systems/Ecosystem/utils/getProjectImage'; + +type ProjectInfo = { + name?: string; + image?: string; + isLoading: boolean; +}; + +// Cache for project data to prevent repeated fetches +const projectsCache = new Map(); + +export function useEcosystemProject(contractId?: string) { + const [projectInfo, setProjectInfo] = useState(() => { + return contractId && projectsCache.has(contractId) + ? projectsCache.get(contractId)! + : { isLoading: false }; + }); + const [error, setError] = useState(); + const isFetching = useRef(false); + + useEffect(() => { + let mounted = true; + + async function fetchProjectInfo() { + if (!contractId || isFetching.current) return; + + // Check cache first + if (projectsCache.has(contractId)) { + setProjectInfo(projectsCache.get(contractId)!); + return; + } + + isFetching.current = true; + setProjectInfo((prev) => ({ ...prev, isLoading: true })); + + try { + const projects = await EcosystemService.fetchProjects(); + + if (!mounted) return; + + if (!projects || !Array.isArray(projects)) { + const info = { isLoading: false }; + setProjectInfo(info); + projectsCache.set(contractId, info); + return; + } + + let found = false; + for (const project of projects) { + if (!project.contracts?.mainnet) continue; + + const contractInfo = project.contracts.mainnet.find( + (c) => c.id?.toLowerCase() === contractId.toLowerCase() + ); + + if (contractInfo) { + const info = { + name: contractInfo.name || project.name, + image: project.image ? getProjectImage(project.image) : undefined, + isLoading: false, + }; + setProjectInfo(info); + projectsCache.set(contractId, info); + setError(undefined); + found = true; + break; + } + } + + if (!found && mounted) { + const info = { isLoading: false }; + setProjectInfo(info); + projectsCache.set(contractId, info); + } + } catch (err) { + if (mounted) { + setError(err instanceof Error ? err.message : 'Unknown error'); + const info = { isLoading: false }; + setProjectInfo(info); + projectsCache.set(contractId, info); + } + } finally { + isFetching.current = false; + } + } + + fetchProjectInfo(); + return () => { + mounted = false; + }; + }, [contractId]); + + return { ...projectInfo, error }; +} From a23a78031d01a7fd0af55fb4c207efc1668b3dbb Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 20 Jan 2025 20:41:28 -0300 Subject: [PATCH 13/85] refactor(simplifyTransaction): improve grouping logic for transaction operations by including asset ID for SEND operations and enhancing contract call grouping --- .../Transaction/utils/simplifyTransaction.ts | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts index 8eb84ee649..105b9c6fdf 100644 --- a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts +++ b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts @@ -141,10 +141,14 @@ export function groupSimilarOperations( // Second pass: group similar non-swap operations const groups = result.reduce( (acc, op) => { - const key = - op.type === TxCategory.CONTRACTCALL - ? op.type // Group all contract calls together - : `${op.type}-${op.to}`; // Other operations grouped by type and destination + let key: string; + if (op.type === TxCategory.CONTRACTCALL) { + key = `${op.type}-${op.to}`; // Group contract calls by their target contract + } else if (op.type === TxCategory.SEND && op.assetId) { + key = `${op.type}-${op.assetId}`; // Group transfers by asset ID + } else { + key = `${op.type}-${op.to}`; // Other operations grouped by type and destination + } if (!acc[key]) { acc[key] = []; } @@ -158,18 +162,22 @@ export function groupSimilarOperations( return Object.values(groups).map((group) => { if (group.length === 1) return group[0]; + const firstOp = group[0]; // Combine similar operations return { - ...group[0], - groupId: `group-${group[0].type}${group[0].type !== TxCategory.CONTRACTCALL ? `-${group[0].to}` : ''}`, + ...firstOp, + groupId: + firstOp.type === TxCategory.SEND && firstOp.assetId + ? `group-${firstOp.type}-${firstOp.assetId}` + : `group-${firstOp.type}-${firstOp.to}`, metadata: { - ...group[0].metadata, + ...firstOp.metadata, operationCount: group.length, // Sum amounts if they exist and are the same asset totalAmount: group.every( - (op) => op.amount && op.assetId === group[0].assetId + (op) => op.amount && op.assetId === firstOp.assetId ) - ? group.reduce((sum, op) => sum.add(op.amount!), group[0].amount!) + ? group.reduce((sum, op) => sum.add(op.amount!), firstOp.amount!) : undefined, }, }; From 05aa994bae9d31ce59b76ae75abe15faa1d8cb78 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Tue, 21 Jan 2025 13:52:02 -0300 Subject: [PATCH 14/85] refactor(TxFeeSimple): simplify component structure by removing Card wrapper and enhancing layout with Box.Stack for improved readability --- .../components/TxViewSimple/TxFeeSimple.tsx | 37 ++++++------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx index a43797d315..3cbd29e6ae 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx @@ -26,50 +26,37 @@ export function TxFeeSimple({ fee, isLoading }: TxFeeSimpleProps) { }; return ( - - - - Network Fee - - - - + + + Network Fee + + + ); } TxFeeSimple.Loader = function TxFeeSimpleLoader() { return ( - + - + ); }; const styles = { - root: cssObj({ - padding: '$3', - }), content: cssObj({ display: 'flex', flexDirection: 'column', gap: '$3', + padding: '$3', }), - row: cssObj({ - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - }), - total: cssObj({ - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - borderTop: '1px solid $gray4', - paddingTop: '$3', - marginTop: '$2', + title: cssObj({ + fontSize: '$sm', + fontWeight: '$medium', }), }; From f3009c6a6d19a0a73a9748cd5e1c8c7f451f9136 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Tue, 21 Jan 2025 13:52:13 -0300 Subject: [PATCH 15/85] feat(TxOperationSend): enhance transaction display by adding account name resolution, avatar support, and improved asset formatting --- .../TxOperationsSimple/TxOperationSend.tsx | 76 ++++++++++++------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSend.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSend.tsx index eee942da2e..a89a941d37 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSend.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSend.tsx @@ -1,7 +1,7 @@ import { cssObj } from '@fuel-ui/css'; -import { Box, Icon, Text } from '@fuel-ui/react'; -import { AssetsAmount } from '~/systems/Asset'; -import { shortAddress } from '~/systems/Core'; +import { Avatar, Box, Icon, IconButton, Text } from '@fuel-ui/react'; +import { useAccounts } from '~/systems/Account'; +import { formatAmount, shortAddress } from '~/systems/Core'; import type { SimplifiedOperation } from '../../../types'; import { TxCategory } from '../../../types'; @@ -10,15 +10,39 @@ type TxOperationSendProps = { }; export function TxOperationSend({ operation }: TxOperationSendProps) { + const { accounts } = useAccounts(); const amount = operation.metadata?.totalAmount || operation.amount || '0'; const operationCount = operation.metadata?.operationCount; const isContractCall = operation.type === TxCategory.CONTRACTCALL; + // Find if addresses match our accounts + const fromAccount = accounts?.find( + (acc) => acc.address.toLowerCase() === operation.from.toLowerCase() + ); + const toAccount = accounts?.find( + (acc) => acc.address.toLowerCase() === operation.to.toLowerCase() + ); + return ( + + + + + + {fromAccount ? fromAccount.name : shortAddress(operation.from)} + + navigator.clipboard.writeText(operation.from)} + /> + - {isContractCall && ( + {isContractCall && !toAccount && ( Calls contract (sending tokens) @@ -30,34 +54,32 @@ export function TxOperationSend({ operation }: TxOperationSendProps) { alignItems: 'center', }} > - + + + {formatAmount({ + amount, + options: { units: 9 }, + })}{' '} + {shortAddress(operation.assetId)} + {operationCount && operationCount > 1 ? ( x{operationCount} ) : null} + + + {toAccount ? toAccount.name : shortAddress(operation.to)} + + navigator.clipboard.writeText(operation.to)} + /> + @@ -67,7 +89,7 @@ export function TxOperationSend({ operation }: TxOperationSendProps) { const styles = { line: cssObj({ display: 'flex', - alignItems: 'center', + alignItems: 'flex-start', gap: '$3', }), iconCol: cssObj({ From b3492e3ab814efb6629e83280c6ad54354f64db1 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Tue, 21 Jan 2025 13:52:21 -0300 Subject: [PATCH 16/85] refactor(TxOperationSwap): remove redundant address display and copy functionality for cleaner UI --- .../TxOperationsSimple/TxOperationSwap.tsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSwap.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSwap.tsx index e182ebef79..1fb8da5833 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSwap.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSwap.tsx @@ -47,16 +47,6 @@ export function TxOperationSwap({ operation }: TxOperationSwapProps) { Contract - - {shortAddress(operation.to)} - - navigator.clipboard.writeText(operation.to)} - /> From 0084c328096df0999449921fcdb23863834ec75c Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Wed, 22 Jan 2025 13:53:00 -0300 Subject: [PATCH 17/85] feat: break into smaller components --- .../components/TxViewSimple/TxFeeSimple.tsx | 2 +- .../TxOperationsSimple/TxOperationsList.tsx | 85 ++++++++++ .../operations/TxOperationContractAsset.tsx | 132 ++++++++++++++++ .../operations/TxOperationExternalCalls.tsx | 101 ++++++++++++ .../operations/TxOperationGroupedCalls.tsx | 145 ++++++++++++++++++ .../operations/TxOperationTransfer.tsx | 100 ++++++++++++ .../components/TxViewSimple/TxViewSimple.tsx | 22 +-- .../hooks/useSimplifyTransaction.ts | 11 ++ .../Transaction/hooks/useTxMetadata.tsx | 2 +- .../Transaction/utils/simplifyTransaction.ts | 37 +++-- 10 files changed, 606 insertions(+), 31 deletions(-) create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContractAsset.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationExternalCalls.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationGroupedCalls.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx create mode 100644 packages/app/src/systems/Transaction/hooks/useSimplifyTransaction.ts diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx index 3cbd29e6ae..63e7832863 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx @@ -1,5 +1,5 @@ import { cssObj } from '@fuel-ui/css'; -import { Box, Card, ContentLoader, Text } from '@fuel-ui/react'; +import { Box, ContentLoader, Text } from '@fuel-ui/react'; import type { AssetFuelAmount } from '@fuel-wallet/types'; import { AssetsAmount } from '~/systems/Asset'; import type { SimplifiedFee } from '../../types'; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx new file mode 100644 index 0000000000..0aca05bdb3 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx @@ -0,0 +1,85 @@ +import { Box } from '@fuel-ui/react'; +import type { SimplifiedOperation } from '../../../types'; +import { TxCategory } from '../../../types'; +import { TxOperationContractAsset } from './operations/TxOperationContractAsset'; +import { TxOperationExternalCalls } from './operations/TxOperationExternalCalls'; +import { TxOperationGroupedCalls } from './operations/TxOperationGroupedCalls'; +import { TxOperationTransfer } from './operations/TxOperationTransfer'; + +type TxOperationsListProps = { + operations: SimplifiedOperation[]; +}; + +export function TxOperationsList({ operations }: TxOperationsListProps) { + console.log('TxOperationsList operations:', operations); // Debug log + return ( + + {operations.map((operation) => { + // If it's a transfer operation + if (operation.type === TxCategory.SEND) { + return ( + + ); + } + + // If it's a contract call with asset transfer + if (operation.type === TxCategory.CONTRACTCALL && operation.amount) { + return ( + + ); + } + + // If it's a grouped contract call + if ( + operation.type === TxCategory.CONTRACTCALL && + operation.metadata?.operationCount && + operation.metadata.operationCount > 1 + ) { + return ( + + ); + } + + // If it's an external contract call + if (operation.type === TxCategory.CONTRACTCALL) { + return ( + + ); + } + + return null; + })} + + ); +} + +// Add loader component +TxOperationsList.Loader = function TxOperationsListLoader() { + return ( + + {[1, 2].map((i) => ( + + ))} + + ); +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContractAsset.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContractAsset.tsx new file mode 100644 index 0000000000..d0ff685c8f --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContractAsset.tsx @@ -0,0 +1,132 @@ +import { cssObj } from '@fuel-ui/css'; +import { Avatar, Box, Icon, IconButton, Spinner, Text } from '@fuel-ui/react'; +import { useAccounts } from '~/systems/Account'; +import { formatAmount, shortAddress } from '~/systems/Core'; +import type { SimplifiedOperation } from '../../../../types'; +import { useEcosystemProject } from '../useEcosystemProject'; + +type TxOperationContractAssetProps = { + operation: SimplifiedOperation; +}; + +export function TxOperationContractAsset({ + operation, +}: TxOperationContractAssetProps) { + const { accounts } = useAccounts(); + const amount = operation.metadata?.totalAmount || operation.amount || '0'; + const { + name: projectName, + image: projectImage, + isLoading, + } = useEcosystemProject(operation.to); + + const toAccount = accounts?.find( + (acc) => acc.address.toLowerCase() === operation.to.toLowerCase() + ); + + const isReceiving = toAccount !== undefined; + + return ( + + + {isLoading ? ( + + ) : projectImage ? ( + + ) : ( + + )} + + + + + {isLoading + ? 'Loading...' + : projectName || shortAddress(operation.to)} + + {!toAccount && ( + + + Contract + + + )} + navigator.clipboard.writeText(operation.to)} + /> + + + + + + + + {isReceiving ? 'Receives token' : 'Sends token'} + + + + + {formatAmount({ + amount, + options: { units: 9 }, + })}{' '} + {shortAddress(operation.assetId)} + + + + + + + ); +} + +const styles = { + root: cssObj({ + display: 'flex', + alignItems: 'flex-start', + gap: '$3', + padding: '$3', + backgroundColor: '$cardBg', + borderRadius: '$md', + }), + iconCol: cssObj({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '24px', + flexShrink: 0, + }), + contentCol: cssObj({ + display: 'flex', + flex: 1, + }), + badge: cssObj({ + backgroundColor: '$gray3', + padding: '$1 $2', + borderRadius: '$md', + marginLeft: '$2', + }), + line: cssObj({ + display: 'flex', + alignItems: 'center', + gap: '$3', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationExternalCalls.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationExternalCalls.tsx new file mode 100644 index 0000000000..3e9efb598c --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationExternalCalls.tsx @@ -0,0 +1,101 @@ +import { cssObj } from '@fuel-ui/css'; +import { Avatar, Box, Icon, IconButton, Spinner, Text } from '@fuel-ui/react'; +import { shortAddress } from '~/systems/Core'; +import type { SimplifiedOperation } from '../../../../types'; +import { useEcosystemProject } from '../useEcosystemProject'; + +type TxOperationExternalCallsProps = { + operation: SimplifiedOperation; +}; + +export function TxOperationExternalCalls({ + operation, +}: TxOperationExternalCallsProps) { + const { + name: projectName, + image: projectImage, + isLoading, + } = useEcosystemProject(operation.to); + + return ( + + + {isLoading ? ( + + ) : projectImage ? ( + + ) : ( + + )} + + + + + {isLoading + ? 'Loading...' + : projectName || shortAddress(operation.to)} + + + + Contract + + + navigator.clipboard.writeText(operation.to)} + /> + + + + + + + + Contract interaction + + + + + + ); +} + +const styles = { + root: cssObj({ + display: 'flex', + alignItems: 'flex-start', + gap: '$3', + padding: '$3', + backgroundColor: '$cardBg', + borderRadius: '$md', + }), + iconCol: cssObj({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '24px', + flexShrink: 0, + }), + contentCol: cssObj({ + display: 'flex', + flex: 1, + }), + badge: cssObj({ + backgroundColor: '$gray3', + padding: '$1 $2', + borderRadius: '$md', + marginLeft: '$2', + }), + line: cssObj({ + display: 'flex', + alignItems: 'center', + gap: '$3', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationGroupedCalls.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationGroupedCalls.tsx new file mode 100644 index 0000000000..70c4885877 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationGroupedCalls.tsx @@ -0,0 +1,145 @@ +import { cssObj } from '@fuel-ui/css'; +import { + Alert, + Avatar, + Box, + Icon, + IconButton, + Spinner, + Text, +} from '@fuel-ui/react'; +import { formatAmount, shortAddress } from '~/systems/Core'; +import type { SimplifiedOperation } from '../../../../types'; +import { useEcosystemProject } from '../useEcosystemProject'; +import { isContractCallMetadata } from '../utils'; + +type TxOperationGroupedCallsProps = { + operation: SimplifiedOperation; +}; + +export function TxOperationGroupedCalls({ + operation, +}: TxOperationGroupedCallsProps) { + const metadata = operation.metadata; + const amount = metadata?.totalAmount || operation.amount || '0'; + const { + name: projectName, + image: projectImage, + isLoading, + } = useEcosystemProject(operation.to); + + if (!isContractCallMetadata(metadata) || !metadata.operationCount) { + return null; + } + + return ( + + + {isLoading ? ( + + ) : projectImage ? ( + + ) : ( + + )} + + + + + {isLoading + ? 'Loading...' + : projectName || shortAddress(operation.to)} + + + + Contract + + + navigator.clipboard.writeText(operation.to)} + /> + + + + This contract call occurs {metadata.operationCount} times + + + + + + + + + Sends token + + + + + {formatAmount({ + amount, + options: { units: 9 }, + })}{' '} + {shortAddress(operation.assetId)} + + + + + + + ); +} + +const styles = { + root: cssObj({ + display: 'flex', + alignItems: 'flex-start', + gap: '$3', + padding: '$3', + backgroundColor: '$cardBg', + borderRadius: '$md', + }), + iconCol: cssObj({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '24px', + flexShrink: 0, + }), + contentCol: cssObj({ + display: 'flex', + flex: 1, + }), + badge: cssObj({ + backgroundColor: '$gray3', + padding: '$1 $2', + borderRadius: '$md', + marginLeft: '$2', + }), + line: cssObj({ + display: 'flex', + alignItems: 'center', + gap: '$3', + }), + alert: cssObj({ + padding: '0', + textAlign: 'center', + backgroundColor: '#E6F4FE', + color: '#0D74CE', + border: 'none', + borderRadius: '6px', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx new file mode 100644 index 0000000000..10713a5d55 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx @@ -0,0 +1,100 @@ +import { cssObj } from '@fuel-ui/css'; +import { Avatar, Box, Icon, IconButton, Text } from '@fuel-ui/react'; +import { useAccounts } from '~/systems/Account'; +import { formatAmount, shortAddress } from '~/systems/Core'; +import type { SimplifiedOperation } from '../../../../types'; + +type TxOperationTransferProps = { + operation: SimplifiedOperation; +}; + +export function TxOperationTransfer({ operation }: TxOperationTransferProps) { + const { accounts } = useAccounts(); + const amount = operation.metadata?.totalAmount || operation.amount || '0'; + const operationCount = operation.metadata?.operationCount; + + // Find if addresses match our accounts + const fromAccount = accounts?.find( + (acc) => acc.address.toLowerCase() === operation.from.toLowerCase() + ); + const toAccount = accounts?.find( + (acc) => acc.address.toLowerCase() === operation.to.toLowerCase() + ); + + return ( + + + + + + + + {fromAccount ? fromAccount.name : shortAddress(operation.from)} + + navigator.clipboard.writeText(operation.from)} + /> + + + + + {formatAmount({ + amount, + options: { units: 9 }, + })}{' '} + {shortAddress(operation.assetId)} + + {operationCount && operationCount > 1 ? ( + + x{operationCount} + + ) : null} + + + + {toAccount ? toAccount.name : shortAddress(operation.to)} + + navigator.clipboard.writeText(operation.to)} + /> + + + + ); +} + +const styles = { + root: cssObj({ + display: 'flex', + alignItems: 'flex-start', + gap: '$3', + padding: '$3', + backgroundColor: '$cardBg', + borderRadius: '$md', + }), + iconCol: cssObj({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '24px', + flexShrink: 0, + }), + contentCol: cssObj({ + display: 'flex', + flex: 1, + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx index 7077fd0fc9..010d25eeec 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx @@ -1,9 +1,10 @@ import { cssObj } from '@fuel-ui/css'; import { Box } from '@fuel-ui/react'; +import { useSimplifyTransaction } from '../../hooks/useSimplifyTransaction'; import type { SimplifiedTransactionViewProps } from '../../types'; import { TxFeeSimple } from './TxFeeSimple'; import { TxHeaderSimple } from './TxHeaderSimple'; -import { TxOperationsSimple } from './TxOperationsSimple'; +import { TxOperationsList } from './TxOperationsSimple/TxOperationsList'; export function TxViewSimple({ transaction, @@ -11,6 +12,10 @@ export function TxViewSimple({ isLoading, footer, }: SimplifiedTransactionViewProps) { + const { simplifiedOperations } = useSimplifyTransaction( + transaction.operations + ); + return ( - + {showDetails && ( )} @@ -34,14 +36,14 @@ export function TxViewSimple({ const styles = { root: cssObj({ + height: '100%', display: 'flex', flexDirection: 'column', }), content: cssObj({ - padding: '$1', - display: 'flex', - flexDirection: 'column', - gap: '$1', + flex: 1, + overflowY: 'auto', + padding: '$4', }), }; @@ -50,7 +52,7 @@ TxViewSimple.Loader = function TxViewSimpleLoader() { return ( - + ); diff --git a/packages/app/src/systems/Transaction/hooks/useSimplifyTransaction.ts b/packages/app/src/systems/Transaction/hooks/useSimplifyTransaction.ts new file mode 100644 index 0000000000..eb9d44129f --- /dev/null +++ b/packages/app/src/systems/Transaction/hooks/useSimplifyTransaction.ts @@ -0,0 +1,11 @@ +import type { Operation } from 'fuels'; +import { useMemo } from 'react'; +import { groupSimilarOperations } from '../utils/simplifyTransaction'; + +export function useSimplifyTransaction(operations: Operation[]) { + const simplifiedOperations = useMemo(() => { + return groupSimilarOperations(operations); + }, [operations]); + + return { simplifiedOperations }; +} diff --git a/packages/app/src/systems/Transaction/hooks/useTxMetadata.tsx b/packages/app/src/systems/Transaction/hooks/useTxMetadata.tsx index d3e2ddaf0e..d9b5936eb4 100644 --- a/packages/app/src/systems/Transaction/hooks/useTxMetadata.tsx +++ b/packages/app/src/systems/Transaction/hooks/useTxMetadata.tsx @@ -3,7 +3,7 @@ import { Address, OperationName } from 'fuels'; import { useMemo } from 'react'; import { useAccounts } from '~/systems/Account'; -import { OperationDirection } from '../types'; +import { OperationDirection } from '../types.tsx'; import { formatDate, getOperationDirection } from '../utils'; type UseTxMetadataProps = { diff --git a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts index 105b9c6fdf..c0d49acc90 100644 --- a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts +++ b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts @@ -5,14 +5,8 @@ import { TransactionStatus, bn, } from 'fuels'; -import { - type ContractCallMetadata, - type SimplifiedFee, - type SimplifiedOperation, - type SimplifiedTransaction, - type SwapMetadata, - TxCategory, -} from '../types'; +import { type SimplifiedOperation, TxCategory } from '../types'; +import type { SimplifiedFee, SimplifiedTransaction } from '../types.tsx'; // Type for transaction request with optional origin properties type TransactionRequestWithOrigin = TransactionRequest & { @@ -40,6 +34,7 @@ function transformOperation( currentAccount?: string ): SimplifiedOperation { const { name, from, to, assetsSent = [], calls = [] } = operation; + const type = getOperationType(operation); // Determine if this operation is from the current account const isFromCurrentAccount = currentAccount @@ -50,7 +45,8 @@ function transformOperation( if (name === OperationName.contractCall && calls.length > 0) { const call = calls[0] as OperationFunctionCall; // Take first call for now, we'll group them later return { - type: TxCategory.CONTRACTCALL, + type, + groupId: `${type}-${to?.address}`, from: from?.address || '', to: to?.address || '', isFromCurrentAccount, @@ -68,7 +64,8 @@ function transformOperation( if (assetsSent.length > 0) { const asset = assetsSent[0]; // Take first asset for now, we'll group them later return { - type: TxCategory.SEND, + type, + groupId: `${type}-${asset.assetId}`, from: from?.address || '', to: to?.address || '', amount: asset.amount ? bn(asset.amount) : undefined, @@ -79,7 +76,8 @@ function transformOperation( // Default case return { - type: getOperationType(operation), + type, + groupId: `${type}-${to?.address}`, from: from?.address || '', to: to?.address || '', isFromCurrentAccount, @@ -211,26 +209,23 @@ export function simplifyTransaction( currentAccount?: string ): SimplifiedTransaction { // Transform operations + console.log('summary', summary); + const operations = transformOperations(summary, currentAccount); // Group similar operations const groupedOperations = groupSimilarOperations(operations); - // Sort operations (current account's operations first) - const sortedOperations = groupedOperations.sort((a, b) => { - if (a.isFromCurrentAccount && !b.isFromCurrentAccount) return -1; - if (!a.isFromCurrentAccount && b.isFromCurrentAccount) return 1; - return 0; - }); + // TODO Sort operations (current account's operations first) // Get origin info from the request context if available const requestWithOrigin = request as TransactionRequestWithOrigin; const origin = requestWithOrigin?.origin; const favicon = requestWithOrigin?.favIconUrl; - return { + const simplifiedTransaction: SimplifiedTransaction = { id: summary.id, - operations: sortedOperations, + operations: groupedOperations, status: deriveStatus(summary), timestamp: summary.time ? new Date(summary.time) : undefined, fee: simplifyFee(summary, request), @@ -246,4 +241,8 @@ export function simplifyTransaction( request, }, }; + + console.log('simplifiedTransaction', simplifiedTransaction); + + return simplifiedTransaction; } From 686a4bc148a5c488477084041b71f09107220a4f Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Wed, 22 Jan 2025 17:29:13 -0300 Subject: [PATCH 18/85] feat: new component structure and simplification of code --- .vscode/settings.json | 3 + .../TxOperationsSimple/TxOperationsSimple.tsx | 99 ------------ .../TxViewSimple/TxOperationsSimple.tsx | 53 ------- .../TxOperationsSimple/TxOperationCard.tsx | 97 ------------ .../TxOperationsSimple/TxOperationContent.tsx | 21 --- .../TxOperationsSimple/TxOperationSwap.tsx | 106 ------------- .../TxOperationsSimple/TxOperationsList.tsx | 65 ++------ .../TxViewSimple/TxOperationsSimple/index.ts | 4 - .../operations/TxAddressDisplay.tsx | 84 ++++++++++ .../operations/TxAssetDisplay.tsx | 57 +++++++ .../operations/TxOperationContract.tsx | 97 ++++++++++++ .../operations/TxOperationContractAsset.tsx | 132 ---------------- .../operations/TxOperationExternalCalls.tsx | 101 ------------ .../operations/TxOperationGroupedCalls.tsx | 145 ------------------ .../operations/TxOperationSwap.tsx | 59 +++++++ .../operations/TxOperationTransfer.tsx | 83 ++-------- .../TxOperationsSimple/useAssetSymbols.ts | 45 ------ .../components/TxViewSimple/TxViewSimple.tsx | 7 +- .../hooks/useSimplifyTransaction.ts | 11 -- .../Transaction/utils/simplifyTransaction.ts | 109 ++++++------- 20 files changed, 374 insertions(+), 1004 deletions(-) delete mode 100644 packages/app/src/systems/Transaction/components/TxOperationsSimple/TxOperationsSimple.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationCard.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationContent.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSwap.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetDisplay.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContractAsset.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationExternalCalls.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationGroupedCalls.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationSwap.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/useAssetSymbols.ts delete mode 100644 packages/app/src/systems/Transaction/hooks/useSimplifyTransaction.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 112501c360..8ee29ee374 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,4 +15,7 @@ "typescript.suggest.paths": true, "typescript.suggest.enabled": true, "typescript.suggest.completeFunctionCalls": true, + "[typescriptreact]": { + "editor.defaultFormatter": "vscode.typescript-language-features" + }, } diff --git a/packages/app/src/systems/Transaction/components/TxOperationsSimple/TxOperationsSimple.tsx b/packages/app/src/systems/Transaction/components/TxOperationsSimple/TxOperationsSimple.tsx deleted file mode 100644 index 2325df4dae..0000000000 --- a/packages/app/src/systems/Transaction/components/TxOperationsSimple/TxOperationsSimple.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { Avatar, Box, Card, ContentLoader, Text } from '@fuel-ui/react'; -import { Address, isB256, isBech32 } from 'fuels'; -import { useAccounts } from '~/systems/Account'; -import { type SimplifiedOperation, TxCategory } from '../../types'; - -export type TxOperationsSimpleProps = { - operations?: SimplifiedOperation[]; - isLoading?: boolean; -}; - -export function TxOperationsSimple({ - operations, - isLoading, -}: TxOperationsSimpleProps) { - const { accounts } = useAccounts(); - if (isLoading) return ; - - return ( - - - {operations?.map((operation, index) => { - const isValidAddress = - isB256(operation.from) || isBech32(operation.from); - const fuelAddress = isValidAddress - ? Address.fromString(operation.from).toString() - : ''; - const name = - accounts?.find((a) => a.address === fuelAddress)?.name || 'unknown'; - - return ( - - - - - {name} - - - - - From: {operation.from} - - - To: {operation.to} - - - - ); - })} - - - ); -} - -TxOperationsSimple.Loader = function TxOperationsSimpleLoader() { - return ( - - - - - - - ); -}; - -const styles = { - operation: cssObj({ - padding: '$3', - }), - content: cssObj({ - display: 'flex', - flexDirection: 'column', - gap: '$3', - }), - info: cssObj({ - display: 'flex', - flexDirection: 'column', - gap: '$2', - }), - type: cssObj({ - display: 'flex', - alignItems: 'center', - gap: '$2', - }), - addresses: cssObj({ - display: 'flex', - flexDirection: 'column', - gap: '$1', - }), - amount: cssObj({ - alignSelf: 'flex-end', - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple.tsx deleted file mode 100644 index ea2ca95384..0000000000 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { Box, Card, ContentLoader } from '@fuel-ui/react'; -import type { SimplifiedOperation } from '../../types'; -import { TxOperationCard } from './TxOperationsSimple/TxOperationCard'; - -export type TxOperationsSimpleProps = { - operations?: SimplifiedOperation[]; - isLoading?: boolean; -}; - -export function TxOperationsSimple({ - operations, - isLoading, -}: TxOperationsSimpleProps) { - if (isLoading) return ; - - return ( - - {operations?.map((operation, index) => ( - - ))} - - ); -} - -TxOperationsSimple.Loader = function TxOperationsSimpleLoader() { - return ( - - - - - - - ); -}; - -const styles = { - operation: cssObj({ - padding: '$1', - }), - content: cssObj({ - display: 'flex', - flexDirection: 'column', - gap: '$3', - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationCard.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationCard.tsx deleted file mode 100644 index f44a347973..0000000000 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationCard.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { - Alert, - Box, - Card, - Drawer, - Icon, - IconButton, - Text, -} from '@fuel-ui/react'; -import { useState } from 'react'; -import type { SimplifiedOperation } from '../../../types'; -import { TxCategory } from '../../../types'; -import { TxOperationContent } from './TxOperationContent'; -import { TxOperationHeader } from './TxOperationHeader'; -import { isContractCallMetadata } from './utils'; - -export type TxOperationCardProps = { - operation: SimplifiedOperation; - index: number; -}; - -export function TxOperationCard({ operation, index }: TxOperationCardProps) { - const [_openDrawer, setOpenDrawer] = useState(null); - const metadata = operation.metadata; - const isGroup = - isContractCallMetadata(metadata) && - metadata.operationCount && - metadata.operationCount > 1 && - operation.groupId?.includes(operation.to); - const key = - operation.groupId || - `${operation.type}-${operation.from}-${operation.to}-${index}`; - - const isContractCall = operation.type === TxCategory.CONTRACTCALL; - const isClickable = isContractCall && isGroup; - - return ( - - isClickable && setOpenDrawer(key)}> - {isGroup && ( - - - This contract call occurs {metadata.operationCount} times - - - )} - - - {!isGroup && } - - - - ); -} - -const styles = { - info: cssObj({ - display: 'flex', - flexDirection: 'column', - gap: '$3', - cursor: ({ isClickable }: { isClickable: boolean }) => - isClickable ? 'pointer' : 'default', - '&:hover': { - backgroundColor: ({ isClickable }: { isClickable: boolean }) => - isClickable ? '$gray2' : 'transparent', - }, - padding: '$3', - }), - alert: cssObj({ - padding: '0', - textAlign: 'center', - backgroundColor: '#E6F4FE', - color: '#0D74CE', - border: 'none', - borderRadius: '6px', - }), - drawer: cssObj({ - display: 'grid', - height: '100%', - gridTemplateRows: '50px 1fr', - }), - drawerHeader: cssObj({ - px: '$4', - py: '$3', - alignItems: 'center', - justifyContent: 'space-between', - borderBottom: '1px solid $border', - fontWeight: '$normal', - }), - drawerContent: cssObj({ - padding: '$4', - display: 'flex', - flexDirection: 'column', - gap: '$3', - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationContent.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationContent.tsx deleted file mode 100644 index 59a12d7a73..0000000000 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationContent.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Box } from '@fuel-ui/react'; -import type { SimplifiedOperation } from '../../../types'; -import { TxCategory } from '../../../types'; -import { TxOperationSend } from './TxOperationSend'; -import { TxOperationSwap } from './TxOperationSwap'; - -type TxOperationContentProps = { - operation: SimplifiedOperation; -}; - -export function TxOperationContent({ operation }: TxOperationContentProps) { - // Don't render content for contract calls - this is handled by the card - if (operation.type === TxCategory.CONTRACTCALL) return null; - - return ( - - - - - ); -} diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSwap.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSwap.tsx deleted file mode 100644 index 1fb8da5833..0000000000 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSwap.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { Avatar, Box, Icon, IconButton, Spinner, Text } from '@fuel-ui/react'; -import { formatAmount, shortAddress } from '~/systems/Core'; -import type { SimplifiedOperation } from '../../../types'; -import { useAssetSymbols } from './useAssetSymbols'; -import { useEcosystemProject } from './useEcosystemProject'; -import { isSwapMetadata } from './utils'; - -type TxOperationSwapProps = { - operation: SimplifiedOperation; -}; - -export function TxOperationSwap({ operation }: TxOperationSwapProps) { - const { assetSymbols } = useAssetSymbols([operation]); - const { - name: projectName, - image: projectImage, - isLoading, - } = useEcosystemProject(operation.to); - - if (!isSwapMetadata(operation.metadata)) return null; - - return ( - <> - - - {isLoading ? ( - - ) : projectImage ? ( - - ) : ( - - )} - - - - {isLoading - ? 'Loading...' - : projectName || shortAddress(operation.to)} - - - - Contract - - - - - - - - - - - Sends token - - - - - {formatAmount({ - amount: operation.metadata.receiveAmount, - options: { units: 9 }, - })}{' '} - {assetSymbols[operation.metadata.receiveAssetId] || - shortAddress(operation.metadata.receiveAssetId)} - - - - - - ); -} - -const styles = { - line: cssObj({ - display: 'flex', - alignItems: 'center', - gap: '$3', - }), - iconCol: cssObj({ - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - width: '24px', - flexShrink: 0, - }), - contentCol: cssObj({ - display: 'flex', - flex: 1, - }), - badge: cssObj({ - backgroundColor: '$gray3', - padding: '$1 $2', - borderRadius: '$md', - marginLeft: '$2', - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx index 0aca05bdb3..ffa9f33d69 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx @@ -1,9 +1,8 @@ import { Box } from '@fuel-ui/react'; +import { useMemo } from 'react'; import type { SimplifiedOperation } from '../../../types'; import { TxCategory } from '../../../types'; -import { TxOperationContractAsset } from './operations/TxOperationContractAsset'; -import { TxOperationExternalCalls } from './operations/TxOperationExternalCalls'; -import { TxOperationGroupedCalls } from './operations/TxOperationGroupedCalls'; +import { TxOperationContract } from './operations/TxOperationContract'; import { TxOperationTransfer } from './operations/TxOperationTransfer'; type TxOperationsListProps = { @@ -11,61 +10,21 @@ type TxOperationsListProps = { }; export function TxOperationsList({ operations }: TxOperationsListProps) { - console.log('TxOperationsList operations:', operations); // Debug log - return ( - - {operations.map((operation) => { - // If it's a transfer operation - if (operation.type === TxCategory.SEND) { - return ( - - ); - } - - // If it's a contract call with asset transfer - if (operation.type === TxCategory.CONTRACTCALL && operation.amount) { - return ( - - ); - } + const renderedOperations = useMemo(() => { + return operations.map((operation) => { + const key = `${operation.type}-${operation.groupId}-${operation.to}`; - // If it's a grouped contract call - if ( - operation.type === TxCategory.CONTRACTCALL && - operation.metadata?.operationCount && - operation.metadata.operationCount > 1 - ) { - return ( - - ); - } + if (operation.type === TxCategory.SEND) { + return ; + } - // If it's an external contract call - if (operation.type === TxCategory.CONTRACTCALL) { - return ( - - ); - } + return ; + }); + }, [operations]); - return null; - })} - - ); + return {renderedOperations}; } -// Add loader component TxOperationsList.Loader = function TxOperationsListLoader() { return ( diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/index.ts b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/index.ts index 4041aaf0b8..ecb55f59a5 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/index.ts +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/index.ts @@ -1,8 +1,4 @@ -export * from './TxOperationCard'; -export * from './TxOperationContent'; export * from './TxOperationDrawer'; export * from './TxOperationHeader'; export * from './TxOperationSend'; -export * from './TxOperationSwap'; export * from './utils'; -export * from './useAssetSymbols'; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx new file mode 100644 index 0000000000..fceed4318c --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx @@ -0,0 +1,84 @@ +import { cssObj } from '@fuel-ui/css'; +import { Avatar, Box, IconButton, Spinner, Text } from '@fuel-ui/react'; +import { useAccounts } from '~/systems/Account'; +import { shortAddress } from '~/systems/Core'; + +type TxAddressDisplayProps = { + address: string; + name?: string; + image?: string; + isLoading?: boolean; + isContract?: boolean; +}; + +export function TxAddressDisplay({ + address, + name, + image, + isLoading, + isContract, +}: TxAddressDisplayProps) { + const { accounts } = useAccounts(); + const account = accounts?.find( + (acc) => acc.address.toLowerCase() === address.toLowerCase() + ); + + return ( + + + {isLoading ? ( + + ) : image ? ( + + ) : ( + + )} + + + + {isLoading + ? 'Loading...' + : account?.name || name || shortAddress(address)} + + {isContract && ( + + + Contract + + + )} + navigator.clipboard.writeText(address)} + /> + + + ); +} + +const styles = { + root: cssObj({ + display: 'flex', + alignItems: 'flex-start', + gap: '$3', + }), + iconCol: cssObj({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '24px', + flexShrink: 0, + }), + contentCol: cssObj({ + display: 'flex', + flex: 1, + }), + badge: cssObj({ + padding: '$1 $2', + backgroundColor: '$gray3', + borderRadius: '$md', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetDisplay.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetDisplay.tsx new file mode 100644 index 0000000000..5d8d3b5fd8 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetDisplay.tsx @@ -0,0 +1,57 @@ +import { cssObj } from '@fuel-ui/css'; +import { Box, Icon, Text } from '@fuel-ui/react'; +import { formatAmount, shortAddress } from '~/systems/Core'; + +type TxAssetDisplayProps = { + amount: string; + assetId?: string; + label?: string; + showIcon?: boolean; + operationCount?: number; +}; + +export function TxAssetDisplay({ + amount, + assetId, + label, + showIcon = true, + operationCount, +}: TxAssetDisplayProps) { + return ( + + {label && ( + + {label} + + )} + + {showIcon && } + + {formatAmount({ + amount, + options: { units: 9 }, // Default to 9 decimals + })}{' '} + {assetId ? shortAddress(assetId) : 'Unknown Asset'} + {operationCount && operationCount > 1 ? ( + + {' '} + x{operationCount} + + ) : null} + + + + ); +} + +const styles = { + root: cssObj({ + display: 'flex', + flexDirection: 'column', + }), + content: cssObj({ + display: 'flex', + gap: '$2', + alignItems: 'center', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx new file mode 100644 index 0000000000..54f1494481 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx @@ -0,0 +1,97 @@ +import { cssObj } from '@fuel-ui/css'; +import { Alert, Box, Icon, Text } from '@fuel-ui/react'; +import type { SimplifiedOperation } from '../../../../types'; +import { useEcosystemProject } from '../useEcosystemProject'; +import { TxAddressDisplay } from './TxAddressDisplay'; +import { TxAssetDisplay } from './TxAssetDisplay'; + +type TxOperationContractProps = { + operation: SimplifiedOperation; +}; + +export function TxOperationContract({ operation }: TxOperationContractProps) { + const { + name: projectName, + image: projectImage, + isLoading, + } = useEcosystemProject(operation.to); + + const metadata = operation.metadata; + const hasAsset = Boolean(operation.amount); + const isGrouped = metadata?.operationCount && metadata.operationCount > 1; + const amount = (metadata?.totalAmount || operation.amount || '0').toString(); + + return ( + + + + {isGrouped && ( + + + This contract call occurs {metadata.operationCount} times + + + )} + + + + + {hasAsset ? ( + + ) : ( + + Contract interaction + + )} + + + + ); +} + +const styles = { + root: cssObj({ + display: 'flex', + alignItems: 'flex-start', + gap: '$3', + padding: '$3', + backgroundColor: '$cardBg', + borderRadius: '$md', + }), + iconCol: cssObj({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '24px', + flexShrink: 0, + }), + contentCol: cssObj({ + display: 'flex', + flex: 1, + }), + line: cssObj({ + display: 'flex', + alignItems: 'flex-start', + gap: '$3', + }), + alert: cssObj({ + backgroundColor: '$gray3', + border: 'none', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContractAsset.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContractAsset.tsx deleted file mode 100644 index d0ff685c8f..0000000000 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContractAsset.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { Avatar, Box, Icon, IconButton, Spinner, Text } from '@fuel-ui/react'; -import { useAccounts } from '~/systems/Account'; -import { formatAmount, shortAddress } from '~/systems/Core'; -import type { SimplifiedOperation } from '../../../../types'; -import { useEcosystemProject } from '../useEcosystemProject'; - -type TxOperationContractAssetProps = { - operation: SimplifiedOperation; -}; - -export function TxOperationContractAsset({ - operation, -}: TxOperationContractAssetProps) { - const { accounts } = useAccounts(); - const amount = operation.metadata?.totalAmount || operation.amount || '0'; - const { - name: projectName, - image: projectImage, - isLoading, - } = useEcosystemProject(operation.to); - - const toAccount = accounts?.find( - (acc) => acc.address.toLowerCase() === operation.to.toLowerCase() - ); - - const isReceiving = toAccount !== undefined; - - return ( - - - {isLoading ? ( - - ) : projectImage ? ( - - ) : ( - - )} - - - - - {isLoading - ? 'Loading...' - : projectName || shortAddress(operation.to)} - - {!toAccount && ( - - - Contract - - - )} - navigator.clipboard.writeText(operation.to)} - /> - - - - - - - - {isReceiving ? 'Receives token' : 'Sends token'} - - - - - {formatAmount({ - amount, - options: { units: 9 }, - })}{' '} - {shortAddress(operation.assetId)} - - - - - - - ); -} - -const styles = { - root: cssObj({ - display: 'flex', - alignItems: 'flex-start', - gap: '$3', - padding: '$3', - backgroundColor: '$cardBg', - borderRadius: '$md', - }), - iconCol: cssObj({ - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - width: '24px', - flexShrink: 0, - }), - contentCol: cssObj({ - display: 'flex', - flex: 1, - }), - badge: cssObj({ - backgroundColor: '$gray3', - padding: '$1 $2', - borderRadius: '$md', - marginLeft: '$2', - }), - line: cssObj({ - display: 'flex', - alignItems: 'center', - gap: '$3', - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationExternalCalls.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationExternalCalls.tsx deleted file mode 100644 index 3e9efb598c..0000000000 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationExternalCalls.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { Avatar, Box, Icon, IconButton, Spinner, Text } from '@fuel-ui/react'; -import { shortAddress } from '~/systems/Core'; -import type { SimplifiedOperation } from '../../../../types'; -import { useEcosystemProject } from '../useEcosystemProject'; - -type TxOperationExternalCallsProps = { - operation: SimplifiedOperation; -}; - -export function TxOperationExternalCalls({ - operation, -}: TxOperationExternalCallsProps) { - const { - name: projectName, - image: projectImage, - isLoading, - } = useEcosystemProject(operation.to); - - return ( - - - {isLoading ? ( - - ) : projectImage ? ( - - ) : ( - - )} - - - - - {isLoading - ? 'Loading...' - : projectName || shortAddress(operation.to)} - - - - Contract - - - navigator.clipboard.writeText(operation.to)} - /> - - - - - - - - Contract interaction - - - - - - ); -} - -const styles = { - root: cssObj({ - display: 'flex', - alignItems: 'flex-start', - gap: '$3', - padding: '$3', - backgroundColor: '$cardBg', - borderRadius: '$md', - }), - iconCol: cssObj({ - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - width: '24px', - flexShrink: 0, - }), - contentCol: cssObj({ - display: 'flex', - flex: 1, - }), - badge: cssObj({ - backgroundColor: '$gray3', - padding: '$1 $2', - borderRadius: '$md', - marginLeft: '$2', - }), - line: cssObj({ - display: 'flex', - alignItems: 'center', - gap: '$3', - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationGroupedCalls.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationGroupedCalls.tsx deleted file mode 100644 index 70c4885877..0000000000 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationGroupedCalls.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { - Alert, - Avatar, - Box, - Icon, - IconButton, - Spinner, - Text, -} from '@fuel-ui/react'; -import { formatAmount, shortAddress } from '~/systems/Core'; -import type { SimplifiedOperation } from '../../../../types'; -import { useEcosystemProject } from '../useEcosystemProject'; -import { isContractCallMetadata } from '../utils'; - -type TxOperationGroupedCallsProps = { - operation: SimplifiedOperation; -}; - -export function TxOperationGroupedCalls({ - operation, -}: TxOperationGroupedCallsProps) { - const metadata = operation.metadata; - const amount = metadata?.totalAmount || operation.amount || '0'; - const { - name: projectName, - image: projectImage, - isLoading, - } = useEcosystemProject(operation.to); - - if (!isContractCallMetadata(metadata) || !metadata.operationCount) { - return null; - } - - return ( - - - {isLoading ? ( - - ) : projectImage ? ( - - ) : ( - - )} - - - - - {isLoading - ? 'Loading...' - : projectName || shortAddress(operation.to)} - - - - Contract - - - navigator.clipboard.writeText(operation.to)} - /> - - - - This contract call occurs {metadata.operationCount} times - - - - - - - - - Sends token - - - - - {formatAmount({ - amount, - options: { units: 9 }, - })}{' '} - {shortAddress(operation.assetId)} - - - - - - - ); -} - -const styles = { - root: cssObj({ - display: 'flex', - alignItems: 'flex-start', - gap: '$3', - padding: '$3', - backgroundColor: '$cardBg', - borderRadius: '$md', - }), - iconCol: cssObj({ - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - width: '24px', - flexShrink: 0, - }), - contentCol: cssObj({ - display: 'flex', - flex: 1, - }), - badge: cssObj({ - backgroundColor: '$gray3', - padding: '$1 $2', - borderRadius: '$md', - marginLeft: '$2', - }), - line: cssObj({ - display: 'flex', - alignItems: 'center', - gap: '$3', - }), - alert: cssObj({ - padding: '0', - textAlign: 'center', - backgroundColor: '#E6F4FE', - color: '#0D74CE', - border: 'none', - borderRadius: '6px', - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationSwap.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationSwap.tsx new file mode 100644 index 0000000000..3bd6393588 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationSwap.tsx @@ -0,0 +1,59 @@ +import { cssObj } from '@fuel-ui/css'; +import { Box, Icon } from '@fuel-ui/react'; +import type { SimplifiedOperation } from '../../../../types'; +import { useEcosystemProject } from '../useEcosystemProject'; +import { isSwapMetadata } from '../utils'; +import { TxAddressDisplay } from './TxAddressDisplay'; +import { TxAssetDisplay } from './TxAssetDisplay'; + +type TxOperationSwapProps = { + operation: SimplifiedOperation; +}; + +export function TxOperationSwap({ operation }: TxOperationSwapProps) { + const { + name: projectName, + image: projectImage, + isLoading, + } = useEcosystemProject(operation.to); + + if (!isSwapMetadata(operation.metadata)) return null; + + return ( + <> + + + + + + + + + ); +} + +const styles = { + line: cssObj({ + display: 'flex', + alignItems: 'flex-start', + gap: '$3', + }), + iconCol: cssObj({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '24px', + flexShrink: 0, + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx index 10713a5d55..7cd95c5de9 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx @@ -1,77 +1,31 @@ import { cssObj } from '@fuel-ui/css'; -import { Avatar, Box, Icon, IconButton, Text } from '@fuel-ui/react'; -import { useAccounts } from '~/systems/Account'; -import { formatAmount, shortAddress } from '~/systems/Core'; +import { Box } from '@fuel-ui/react'; import type { SimplifiedOperation } from '../../../../types'; +import { TxAddressDisplay } from './TxAddressDisplay'; +import { TxAssetDisplay } from './TxAssetDisplay'; type TxOperationTransferProps = { operation: SimplifiedOperation; }; export function TxOperationTransfer({ operation }: TxOperationTransferProps) { - const { accounts } = useAccounts(); - const amount = operation.metadata?.totalAmount || operation.amount || '0'; + const amount = ( + operation.metadata?.totalAmount || + operation.amount || + '0' + ).toString(); const operationCount = operation.metadata?.operationCount; - // Find if addresses match our accounts - const fromAccount = accounts?.find( - (acc) => acc.address.toLowerCase() === operation.from.toLowerCase() - ); - const toAccount = accounts?.find( - (acc) => acc.address.toLowerCase() === operation.to.toLowerCase() - ); - return ( - - - - - - {fromAccount ? fromAccount.name : shortAddress(operation.from)} - - navigator.clipboard.writeText(operation.from)} - /> - - - - - {formatAmount({ - amount, - options: { units: 9 }, - })}{' '} - {shortAddress(operation.assetId)} - - {operationCount && operationCount > 1 ? ( - - x{operationCount} - - ) : null} - - - - {toAccount ? toAccount.name : shortAddress(operation.to)} - - navigator.clipboard.writeText(operation.to)} - /> - + + + ); @@ -86,13 +40,6 @@ const styles = { backgroundColor: '$cardBg', borderRadius: '$md', }), - iconCol: cssObj({ - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - width: '24px', - flexShrink: 0, - }), contentCol: cssObj({ display: 'flex', flex: 1, diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/useAssetSymbols.ts b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/useAssetSymbols.ts deleted file mode 100644 index eef35b8f73..0000000000 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/useAssetSymbols.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { useEffect, useState } from 'react'; -import { AssetService } from '~/systems/Asset/services/assets'; -import type { SimplifiedOperation } from '../../../types'; -import { isSwapMetadata } from './utils'; - -export function useAssetSymbols(operations: SimplifiedOperation[]) { - const [assetSymbols, setAssetSymbols] = useState>({}); - - useEffect(() => { - async function loadAssetSymbols() { - const symbols: Record = {}; - if (operations) { - const allAssets = await AssetService.getAssets(); - for (const op of operations) { - if (op.assetId) { - const asset = allAssets.find((a) => - a.networks?.some( - (n) => n.type === 'fuel' && n.assetId === op.assetId - ) - ); - if (asset) { - symbols[op.assetId] = asset.symbol; - } - } - const metadata = op.metadata; - if (isSwapMetadata(metadata)) { - const asset = allAssets.find((a) => - a.networks?.some( - (n) => - n.type === 'fuel' && n.assetId === metadata.receiveAssetId - ) - ); - if (asset) { - symbols[metadata.receiveAssetId] = asset.symbol; - } - } - } - } - setAssetSymbols(symbols); - } - loadAssetSymbols(); - }, [operations]); - - return { assetSymbols }; -} diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx index 010d25eeec..59d3164afe 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx @@ -1,6 +1,5 @@ import { cssObj } from '@fuel-ui/css'; import { Box } from '@fuel-ui/react'; -import { useSimplifyTransaction } from '../../hooks/useSimplifyTransaction'; import type { SimplifiedTransactionViewProps } from '../../types'; import { TxFeeSimple } from './TxFeeSimple'; import { TxHeaderSimple } from './TxHeaderSimple'; @@ -12,10 +11,6 @@ export function TxViewSimple({ isLoading, footer, }: SimplifiedTransactionViewProps) { - const { simplifiedOperations } = useSimplifyTransaction( - transaction.operations - ); - return ( - + {showDetails && ( )} diff --git a/packages/app/src/systems/Transaction/hooks/useSimplifyTransaction.ts b/packages/app/src/systems/Transaction/hooks/useSimplifyTransaction.ts deleted file mode 100644 index eb9d44129f..0000000000 --- a/packages/app/src/systems/Transaction/hooks/useSimplifyTransaction.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { Operation } from 'fuels'; -import { useMemo } from 'react'; -import { groupSimilarOperations } from '../utils/simplifyTransaction'; - -export function useSimplifyTransaction(operations: Operation[]) { - const simplifiedOperations = useMemo(() => { - return groupSimilarOperations(operations); - }, [operations]); - - return { simplifiedOperations }; -} diff --git a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts index c0d49acc90..9ea0ba1911 100644 --- a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts +++ b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts @@ -5,7 +5,12 @@ import { TransactionStatus, bn, } from 'fuels'; -import { type SimplifiedOperation, TxCategory } from '../types'; +import type { + ContractCallMetadata, + SimplifiedOperation, + SwapMetadata, +} from '../types'; +import { TxCategory } from '../types'; import type { SimplifiedFee, SimplifiedTransaction } from '../types.tsx'; // Type for transaction request with optional origin properties @@ -19,7 +24,7 @@ function getOperationType(operation: Operation): TxCategory { switch (name) { case OperationName.transfer: - return TxCategory.SEND; + return operation.to ? TxCategory.SEND : TxCategory.RECEIVE; case OperationName.contractCall: return TxCategory.CONTRACTCALL; case OperationName.script: @@ -43,26 +48,28 @@ function transformOperation( // For contract calls, use the contract information if (name === OperationName.contractCall && calls.length > 0) { - const call = calls[0] as OperationFunctionCall; // Take first call for now, we'll group them later + const call = calls[0] as OperationFunctionCall; + const metadata: ContractCallMetadata = { + contractId: to?.address, + functionName: call.functionName, + functionData: call, + amount: call.amount ? bn(call.amount) : undefined, + assetId: call.assetId, + }; + return { type, groupId: `${type}-${to?.address}`, from: from?.address || '', to: to?.address || '', isFromCurrentAccount, - metadata: { - contractId: to?.address, - functionName: call.functionName, - functionData: call, - amount: call.amount ? bn(call.amount) : undefined, - assetId: call.assetId, - }, + metadata, }; } // For transfers, use the asset information if (assetsSent.length > 0) { - const asset = assetsSent[0]; // Take first asset for now, we'll group them later + const asset = assetsSent[0]; return { type, groupId: `${type}-${asset.assetId}`, @@ -96,57 +103,21 @@ export function transformOperations( export function groupSimilarOperations( operations: SimplifiedOperation[] ): SimplifiedOperation[] { - const result: SimplifiedOperation[] = []; - const used = new Set(); - - // First pass: detect swaps - for (let i = 0; i < operations.length; i++) { - if (used.has(i)) continue; - - const current = operations[i]; - if (current.type === TxCategory.SEND && i + 1 < operations.length) { - const next = operations[i + 1]; - // Check if this is a swap: - // 1. Both operations are sends - // 2. The sender of the first is the receiver of the second - // 3. The receiver of the first is the sender of the second - if ( - next.type === TxCategory.SEND && - current.from === next.to && - current.to === next.from - ) { - // Combine the two operations into one swap - result.push({ - ...current, - metadata: { - isSwap: true, - receiveAmount: next.amount?.toString() || '0', - receiveAssetId: next.assetId || '', - } as SwapMetadata, - }); - used.add(i); - used.add(i + 1); - continue; - } - } - - if (!used.has(i)) { - result.push(current); - used.add(i); - } - } - - // Second pass: group similar non-swap operations - const groups = result.reduce( + // Group operations by type, asset, and destination + const groups = operations.reduce( (acc, op) => { let key: string; if (op.type === TxCategory.CONTRACTCALL) { key = `${op.type}-${op.to}`; // Group contract calls by their target contract - } else if (op.type === TxCategory.SEND && op.assetId) { - key = `${op.type}-${op.assetId}`; // Group transfers by asset ID + } else if ( + (op.type === TxCategory.SEND || op.type === TxCategory.RECEIVE) && + op.assetId + ) { + key = `${op.type}-${op.assetId}-${op.to}`; // Group transfers by type, asset ID, and destination } else { key = `${op.type}-${op.to}`; // Other operations grouped by type and destination } + if (!acc[key]) { acc[key] = []; } @@ -161,21 +132,33 @@ export function groupSimilarOperations( if (group.length === 1) return group[0]; const firstOp = group[0]; - // Combine similar operations + const metadata = firstOp.metadata as ContractCallMetadata; + + // For contract calls, mark as a group + if (firstOp.type === TxCategory.CONTRACTCALL) { + return { + ...firstOp, + metadata: { + ...metadata, + isContractCallGroup: true, + operationCount: group.length, + totalAmount: group.reduce((sum, op) => { + const opMetadata = op.metadata as ContractCallMetadata; + return opMetadata?.amount ? sum.add(opMetadata.amount) : sum; + }, bn(0)), + }, + }; + } + + // For transfers, sum the amounts if they're the same asset return { ...firstOp, - groupId: - firstOp.type === TxCategory.SEND && firstOp.assetId - ? `group-${firstOp.type}-${firstOp.assetId}` - : `group-${firstOp.type}-${firstOp.to}`, metadata: { - ...firstOp.metadata, operationCount: group.length, - // Sum amounts if they exist and are the same asset totalAmount: group.every( (op) => op.amount && op.assetId === firstOp.assetId ) - ? group.reduce((sum, op) => sum.add(op.amount!), firstOp.amount!) + ? group.reduce((sum, op) => sum.add(op.amount!), bn(0)) : undefined, }, }; From f92e013940f7b23651cd39a17b39cf0d289429a1 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Wed, 22 Jan 2025 18:09:53 -0300 Subject: [PATCH 19/85] stlyes: reduce ui gaps --- .../TxOperationsSimple/TxOperationsList.tsx | 4 ++-- .../operations/TxAddressDisplay.tsx | 16 ++++++++-------- .../operations/TxAssetDisplay.tsx | 6 +++--- .../operations/TxOperationContract.tsx | 9 +++++---- .../operations/TxOperationTransfer.tsx | 6 +++--- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx index ffa9f33d69..45aebdf92a 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx @@ -22,12 +22,12 @@ export function TxOperationsList({ operations }: TxOperationsListProps) { }); }, [operations]); - return {renderedOperations}; + return {renderedOperations}; } TxOperationsList.Loader = function TxOperationsListLoader() { return ( - + {[1, 2].map((i) => ( {isLoading ? ( - + ) : image ? ( - + ) : ( - + )} - + {isLoading ? 'Loading...' @@ -62,14 +62,14 @@ export function TxAddressDisplay({ const styles = { root: cssObj({ display: 'flex', - alignItems: 'flex-start', - gap: '$3', + alignItems: 'center', + gap: '$1', }), iconCol: cssObj({ display: 'flex', alignItems: 'center', justifyContent: 'center', - width: '24px', + width: '20px', flexShrink: 0, }), contentCol: cssObj({ @@ -77,7 +77,7 @@ const styles = { flex: 1, }), badge: cssObj({ - padding: '$1 $2', + padding: '0 $1', backgroundColor: '$gray3', borderRadius: '$md', }), diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetDisplay.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetDisplay.tsx index 5d8d3b5fd8..fc0afd149f 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetDisplay.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetDisplay.tsx @@ -18,14 +18,14 @@ export function TxAssetDisplay({ operationCount, }: TxAssetDisplayProps) { return ( - + {label && ( {label} )} - {showIcon && } + {showIcon && } {formatAmount({ amount, @@ -51,7 +51,7 @@ const styles = { }), content: cssObj({ display: 'flex', - gap: '$2', + gap: '$1', alignItems: 'center', }), }; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx index 54f1494481..ff93ec4f33 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx @@ -23,7 +23,7 @@ export function TxOperationContract({ operation }: TxOperationContractProps) { return ( - + - + Date: Wed, 22 Jan 2025 22:38:38 -0300 Subject: [PATCH 20/85] feat(Transaction): enhance transaction types and operations with depth and receipt tracking --- .../operations/TxOperationContract.tsx | 21 ++ .../operations/TxOperationTransfer.tsx | 23 ++- .../app/src/systems/Transaction/types.tsx | 34 +++- .../Transaction/utils/simplifyTransaction.ts | 179 +++++++++++------- 4 files changed, 191 insertions(+), 66 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx index ff93ec4f33..67c9de4588 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx @@ -20,10 +20,17 @@ export function TxOperationContract({ operation }: TxOperationContractProps) { const hasAsset = Boolean(operation.amount); const isGrouped = metadata?.operationCount && metadata.operationCount > 1; const amount = (metadata?.totalAmount || operation.amount || '0').toString(); + const depth = operation.depth || 0; return ( + + {operation.isRoot && ( + + root + + )} + cssObj({ + width: depth ? '2px' : '0', + minWidth: depth ? '2px' : '0', + backgroundColor: '$gray4', + marginLeft: `${depth * 8}px`, + alignSelf: 'stretch', + }), }; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx index a516ae4170..ec6ae96f8f 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx @@ -1,5 +1,5 @@ import { cssObj } from '@fuel-ui/css'; -import { Box } from '@fuel-ui/react'; +import { Box, Text } from '@fuel-ui/react'; import type { SimplifiedOperation } from '../../../../types'; import { TxAddressDisplay } from './TxAddressDisplay'; import { TxAssetDisplay } from './TxAssetDisplay'; @@ -15,10 +15,17 @@ export function TxOperationTransfer({ operation }: TxOperationTransferProps) { '0' ).toString(); const operationCount = operation.metadata?.operationCount; + const depth = operation.depth || 0; return ( + + {operation.isRoot && ( + + root + + )} + cssObj({ + width: depth ? '2px' : '0', + minWidth: depth ? '2px' : '0', + backgroundColor: '$gray4', + marginLeft: `${depth * 8}px`, + alignSelf: 'stretch', + }), }; diff --git a/packages/app/src/systems/Transaction/types.tsx b/packages/app/src/systems/Transaction/types.tsx index 0db494d9e3..ae1e5b5af8 100644 --- a/packages/app/src/systems/Transaction/types.tsx +++ b/packages/app/src/systems/Transaction/types.tsx @@ -7,6 +7,7 @@ import type { InputContract, OutputContract, OutputContractCreated, + Receipt, TransactionRequest, TransactionRequestInput, TransactionRequestLike, @@ -60,6 +61,10 @@ export type ContractCallMetadata = { isContractCallGroup?: boolean; operationCount?: number; totalAmount?: BN; + isRoot?: boolean; + receipts?: Receipt[]; + depth?: number; + parentReceiptId?: string; }; export type SwapMetadata = { @@ -68,6 +73,9 @@ export type SwapMetadata = { receiveAssetId: string; totalAmount?: BN; operationCount?: number; + receipts?: Receipt[]; + depth?: number; + parentReceiptId?: string; }; export type SimplifiedOperation = { @@ -77,7 +85,9 @@ export type SimplifiedOperation = { amount?: BN; assetId?: string; isFromCurrentAccount?: boolean; + isRoot?: boolean; groupId?: string; + depth?: number; metadata?: ContractCallMetadata | SwapMetadata; }; @@ -92,7 +102,6 @@ export type SimplifiedFee = { export type SimplifiedTransaction = { id?: string; operations: SimplifiedOperation[]; - status: TransactionStatus; timestamp?: Date; fee: SimplifiedFee; origin?: { @@ -112,3 +121,26 @@ export type SimplifiedTransactionViewProps = { isLoading?: boolean; footer?: ReactNode; }; + +export interface AssetFlow { + assetId: string; + amount: BN; + from: string; + to: string; + type: 'in' | 'out'; // from perspective of current user +} + +export interface SimplifiedAssetFlows { + assetsIn: AssetFlow[]; + assetsOut: AssetFlow[]; + fees: { + gasUsed: BN; + networkFee: BN; + tip: BN; + otherFees: AssetFlow[]; // Other fees paid in various assets + }; + contractInteractions: Array<{ + contractId: string; + functionName?: string; + }>; +} diff --git a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts index 9ea0ba1911..f2370b549b 100644 --- a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts +++ b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts @@ -2,18 +2,15 @@ import type { Operation, TransactionRequest, TransactionSummary } from 'fuels'; import { type OperationFunctionCall, OperationName, + ReceiptType, + type TransactionResultReceipt, TransactionStatus, bn, } from 'fuels'; -import type { - ContractCallMetadata, - SimplifiedOperation, - SwapMetadata, -} from '../types'; +import type { ContractCallMetadata, SimplifiedOperation } from '../types'; import { TxCategory } from '../types'; import type { SimplifiedFee, SimplifiedTransaction } from '../types.tsx'; -// Type for transaction request with optional origin properties type TransactionRequestWithOrigin = TransactionRequest & { origin?: string; favIconUrl?: string; @@ -34,19 +31,45 @@ function getOperationType(operation: Operation): TxCategory { } } +function isRootOperation(_operation: Operation): boolean { + return false; +} + +function getReceiptDepth( + receipt: TransactionResultReceipt, + allReceipts: TransactionResultReceipt[] +): number { + // For now, use a simple depth calculation based on receipt order + // We can enhance this later when we have better receipt hierarchy info + const index = allReceipts.findIndex((r) => r === receipt); + return index > 0 ? 1 : 0; +} + function transformOperation( operation: Operation, - currentAccount?: string + currentAccount?: string, + allReceipts: TransactionResultReceipt[] = [] ): SimplifiedOperation { - const { name, from, to, assetsSent = [], calls = [] } = operation; + const { + name, + from, + to, + assetsSent = [], + calls = [], + receipts = [], + } = operation; const type = getOperationType(operation); + const isRoot = isRootOperation(operation); + + // Calculate depth based on receipts + const depth = receipts.length + ? Math.min(...receipts.map((r) => getReceiptDepth(r, allReceipts))) + : 0; - // Determine if this operation is from the current account const isFromCurrentAccount = currentAccount ? from?.address === currentAccount : false; - // For contract calls, use the contract information if (name === OperationName.contractCall && calls.length > 0) { const call = calls[0] as OperationFunctionCall; const metadata: ContractCallMetadata = { @@ -55,6 +78,9 @@ function transformOperation( functionData: call, amount: call.amount ? bn(call.amount) : undefined, assetId: call.assetId, + isRoot, + receipts, + depth, }; return { @@ -63,11 +89,12 @@ function transformOperation( from: from?.address || '', to: to?.address || '', isFromCurrentAccount, + isRoot, + depth, metadata, }; } - // For transfers, use the asset information if (assetsSent.length > 0) { const asset = assetsSent[0]; return { @@ -78,16 +105,27 @@ function transformOperation( amount: asset.amount ? bn(asset.amount) : undefined, assetId: asset.assetId, isFromCurrentAccount, + isRoot, + depth, + metadata: { + receipts, + depth, + }, }; } - // Default case return { type, groupId: `${type}-${to?.address}`, from: from?.address || '', to: to?.address || '', isFromCurrentAccount, + isRoot, + depth, + metadata: { + receipts, + depth, + }, }; } @@ -97,27 +135,70 @@ export function transformOperations( ): SimplifiedOperation[] { if (!summary.operations) return []; - return summary.operations.map((op) => transformOperation(op, currentAccount)); + // Get all receipts from all operations for depth calculation + const allReceipts = summary.operations.flatMap((op) => op.receipts || []); + + // Transform all operations but only keep ones relevant to current account + const operations = summary.operations + .filter((op) => { + if (!currentAccount) return true; + const currentAccountLower = currentAccount.toLowerCase(); + + // Check operation addresses + const opTo = op.to?.address?.toLowerCase(); + const opFrom = op.from?.address?.toLowerCase(); + const isOperationRelevant = + opTo === currentAccountLower || opFrom === currentAccountLower; + + // Check receipt addresses + const hasRelevantReceipt = op.receipts?.some((receipt) => { + // Only check transfer receipts for now as they have from/to fields + if ( + receipt.type === ReceiptType.Transfer || + receipt.type === ReceiptType.TransferOut + ) { + const transfer = receipt as { to?: string; from?: string }; + return ( + transfer.to?.toLowerCase() === currentAccountLower || + transfer.from?.toLowerCase() === currentAccountLower + ); + } + return false; + }); + + // Show operation if either the operation itself or any of its receipts involve the current account + return isOperationRelevant || hasRelevantReceipt; + }) + .map((op) => { + const transformed = transformOperation(op, currentAccount, allReceipts); + console.log('Operation:', { + type: op.name, + receipts: op.receipts?.map((r) => r.type), + isRoot: transformed.isRoot, + depth: transformed.depth, + from: op.from?.address, + to: op.to?.address, + receiptTypes: op.receipts?.map((r) => r.type), + }); + return transformed; + }); + + // Sort operations by depth + return operations.sort((a, b) => (a.depth || 0) - (b.depth || 0)); +} + +function getGroupKey(op: SimplifiedOperation): string { + const base = `${op.type}-${op.to}`; + if (op.type === TxCategory.CONTRACTCALL) return base; + return op.assetId ? `${base}-${op.assetId}` : base; } export function groupSimilarOperations( operations: SimplifiedOperation[] ): SimplifiedOperation[] { - // Group operations by type, asset, and destination const groups = operations.reduce( (acc, op) => { - let key: string; - if (op.type === TxCategory.CONTRACTCALL) { - key = `${op.type}-${op.to}`; // Group contract calls by their target contract - } else if ( - (op.type === TxCategory.SEND || op.type === TxCategory.RECEIVE) && - op.assetId - ) { - key = `${op.type}-${op.assetId}-${op.to}`; // Group transfers by type, asset ID, and destination - } else { - key = `${op.type}-${op.to}`; // Other operations grouped by type and destination - } - + const key = getGroupKey(op); if (!acc[key]) { acc[key] = []; } @@ -127,14 +208,12 @@ export function groupSimilarOperations( {} as Record ); - // Combine operations in each group return Object.values(groups).map((group) => { if (group.length === 1) return group[0]; const firstOp = group[0]; const metadata = firstOp.metadata as ContractCallMetadata; - // For contract calls, mark as a group if (firstOp.type === TxCategory.CONTRACTCALL) { return { ...firstOp, @@ -150,7 +229,6 @@ export function groupSimilarOperations( }; } - // For transfers, sum the amounts if they're the same asset return { ...firstOp, metadata: { @@ -165,53 +243,30 @@ export function groupSimilarOperations( }); } -export function simplifyFee( - summary: TransactionSummary, - request?: TransactionRequest -): SimplifiedFee { - const tip = request?.tip || bn(0); - return { - total: summary.fee || bn(0), - network: (summary.fee || bn(0)).sub(tip), - tip, - gasUsed: summary.gasUsed || bn(0), - gasPrice: bn(0), // This will be calculated later when we have access to the provider - }; -} - -function deriveStatus(summary: TransactionSummary): TransactionStatus { - if (summary.isStatusSuccess) return TransactionStatus.success; - if (summary.isStatusFailure) return TransactionStatus.failure; - if (summary.isStatusPending) return TransactionStatus.submitted; - return TransactionStatus.submitted; // Default to submitted if no status is set -} - export function simplifyTransaction( summary: TransactionSummary, request?: TransactionRequest, currentAccount?: string ): SimplifiedTransaction { - // Transform operations console.log('summary', summary); - const operations = transformOperations(summary, currentAccount); - - // Group similar operations const groupedOperations = groupSimilarOperations(operations); - // TODO Sort operations (current account's operations first) - - // Get origin info from the request context if available const requestWithOrigin = request as TransactionRequestWithOrigin; const origin = requestWithOrigin?.origin; const favicon = requestWithOrigin?.favIconUrl; - const simplifiedTransaction: SimplifiedTransaction = { + return { id: summary.id, operations: groupedOperations, - status: deriveStatus(summary), timestamp: summary.time ? new Date(summary.time) : undefined, - fee: simplifyFee(summary, request), + fee: { + total: bn(summary.fee || 0), + network: bn(summary.fee || 0).sub(bn(request?.tip || 0)), + tip: bn(request?.tip || 0), + gasUsed: bn(summary.gasUsed || 0), + gasPrice: bn(0), // This will be calculated later when we have access to the provider + }, origin: origin ? { name: origin, @@ -224,8 +279,4 @@ export function simplifyTransaction( request, }, }; - - console.log('simplifiedTransaction', simplifiedTransaction); - - return simplifiedTransaction; } From 1f48afcde27b6d15a5aa8b6897645a71bc5cf949 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Thu, 23 Jan 2025 14:52:31 -0300 Subject: [PATCH 21/85] feat: group indent by depth --- .../components/TxViewSimple/TxFeeSimple.tsx | 18 +- .../TxOperationsSimple/TxOperationsList.tsx | 5 +- .../operations/TxAddressDisplay.tsx | 19 +- .../operations/TxAssetAmount.tsx | 76 +++++++ .../operations/TxAssetDisplay.tsx | 44 ++--- .../operations/TxOperationContract.tsx | 108 +++------- .../operations/TxOperationNesting.tsx | 32 +++ .../operations/TxOperationTransfer.tsx | 96 +++++---- .../components/TxViewSimple/TxViewSimple.tsx | 7 +- packages/app/src/systems/Transaction/types.ts | 140 +++++++++++++ .../app/src/systems/Transaction/types.tsx | 17 +- .../Transaction/utils/simplifyTransaction.ts | 185 +++++------------- 12 files changed, 423 insertions(+), 324 deletions(-) create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetAmount.tsx create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationNesting.tsx create mode 100644 packages/app/src/systems/Transaction/types.ts diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx index 63e7832863..1e6277f689 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx @@ -1,8 +1,7 @@ import { cssObj } from '@fuel-ui/css'; import { Box, ContentLoader, Text } from '@fuel-ui/react'; -import type { AssetFuelAmount } from '@fuel-wallet/types'; -import { AssetsAmount } from '~/systems/Asset'; import type { SimplifiedFee } from '../../types'; +import { TxAssetAmount } from './TxOperationsSimple/operations/TxAssetAmount'; type TxFeeSimpleProps = { fee: SimplifiedFee; @@ -12,24 +11,11 @@ type TxFeeSimpleProps = { export function TxFeeSimple({ fee, isLoading }: TxFeeSimpleProps) { if (isLoading) return ; - const { network } = fee; - - const fuelAmount: AssetFuelAmount = { - type: 'fuel', - chainId: 0, - decimals: 9, - assetId: '', - name: 'Fuel', - symbol: 'ETH', - amount: network, - icon: '', - }; - return ( Network Fee - + ); diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx index 45aebdf92a..0c85a871af 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx @@ -11,8 +11,9 @@ type TxOperationsListProps = { export function TxOperationsList({ operations }: TxOperationsListProps) { const renderedOperations = useMemo(() => { - return operations.map((operation) => { - const key = `${operation.type}-${operation.groupId}-${operation.to}`; + return operations.map((operation, index) => { + console.log('Rendering operation:', operation); + const key = `${operation.type}-${operation.from}-${operation.to}-${index}`; if (operation.type === TxCategory.SEND) { return ; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx index 59d4d8d18f..8180c466be 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx @@ -1,5 +1,5 @@ import { cssObj } from '@fuel-ui/css'; -import { Avatar, Box, IconButton, Spinner, Text } from '@fuel-ui/react'; +import { Avatar, Box, IconButton, Text } from '@fuel-ui/react'; import { useAccounts } from '~/systems/Account'; import { shortAddress } from '~/systems/Core'; @@ -7,16 +7,16 @@ type TxAddressDisplayProps = { address: string; name?: string; image?: string; - isLoading?: boolean; isContract?: boolean; + label?: string; }; export function TxAddressDisplay({ address, name, image, - isLoading, isContract, + label, }: TxAddressDisplayProps) { const { accounts } = useAccounts(); const account = accounts?.find( @@ -26,19 +26,20 @@ export function TxAddressDisplay({ return ( - {isLoading ? ( - - ) : image ? ( + {image ? ( ) : ( )} + {label && ( + + {label}: + + )} - {isLoading - ? 'Loading...' - : account?.name || name || shortAddress(address)} + {account?.name || name || shortAddress(address)} {isContract && ( diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetAmount.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetAmount.tsx new file mode 100644 index 0000000000..3f14d57a28 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetAmount.tsx @@ -0,0 +1,76 @@ +import { cssObj } from '@fuel-ui/css'; +import { Box, Icon, Text } from '@fuel-ui/react'; +import { useAsset } from '~/systems/Asset'; +import { formatAmount, shortAddress } from '~/systems/Core'; + +type TxAssetAmountProps = { + amount: string; + assetId?: string; + showIcon?: boolean; + showSymbol?: boolean; + showAssetId?: boolean; + showLabel?: boolean; + label?: string; +}; + +export function TxAssetAmount({ + amount, + assetId, + showIcon = true, + showSymbol = true, + showAssetId = true, + showLabel = true, + label, +}: TxAssetAmountProps) { + const asset = useAsset(assetId); + + return ( + + {showLabel && label && ( + + {label} + + )} + + {showIcon && } + + {formatAmount({ + amount, + options: { units: 9 }, // Default to 9 decimals for ETH + })} + {showSymbol && asset && ( + + {' '} + {asset.symbol || 'Unknown'} + + )} + {showAssetId && assetId && ( + + {' '} + ({shortAddress(assetId)}) + + )} + + + + ); +} + +const styles = { + root: cssObj({ + display: 'flex', + flexDirection: 'column', + }), + content: cssObj({ + display: 'flex', + gap: '$1', + alignItems: 'center', + }), + symbol: cssObj({ + fontWeight: '$semibold', + }), + assetId: cssObj({ + color: '$gray8', + fontSize: '$xs', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetDisplay.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetDisplay.tsx index fc0afd149f..7f7efaf073 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetDisplay.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetDisplay.tsx @@ -1,45 +1,33 @@ import { cssObj } from '@fuel-ui/css'; -import { Box, Icon, Text } from '@fuel-ui/react'; -import { formatAmount, shortAddress } from '~/systems/Core'; +import { Box } from '@fuel-ui/react'; +import { TxAssetAmount } from './TxAssetAmount'; type TxAssetDisplayProps = { amount: string; assetId?: string; label?: string; - showIcon?: boolean; operationCount?: number; + showLabel?: boolean; }; export function TxAssetDisplay({ amount, assetId, label, - showIcon = true, operationCount, + showLabel, }: TxAssetDisplayProps) { return ( - {label && ( - - {label} - + + {operationCount && operationCount > 1 && ( + x{operationCount} )} - - {showIcon && } - - {formatAmount({ - amount, - options: { units: 9 }, // Default to 9 decimals - })}{' '} - {assetId ? shortAddress(assetId) : 'Unknown Asset'} - {operationCount && operationCount > 1 ? ( - - {' '} - x{operationCount} - - ) : null} - - ); } @@ -49,9 +37,9 @@ const styles = { display: 'flex', flexDirection: 'column', }), - content: cssObj({ - display: 'flex', - gap: '$1', - alignItems: 'center', + count: cssObj({ + color: '$gray8', + fontSize: '$xs', + marginLeft: '$1', }), }; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx index 67c9de4588..0ca89bcacd 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx @@ -1,72 +1,40 @@ import { cssObj } from '@fuel-ui/css'; -import { Alert, Box, Icon, Text } from '@fuel-ui/react'; +import { Box, Text } from '@fuel-ui/react'; import type { SimplifiedOperation } from '../../../../types'; -import { useEcosystemProject } from '../useEcosystemProject'; import { TxAddressDisplay } from './TxAddressDisplay'; import { TxAssetDisplay } from './TxAssetDisplay'; +import { TxOperationNesting } from './TxOperationNesting'; type TxOperationContractProps = { operation: SimplifiedOperation; }; export function TxOperationContract({ operation }: TxOperationContractProps) { - const { - name: projectName, - image: projectImage, - isLoading, - } = useEcosystemProject(operation.to); - const metadata = operation.metadata; - const hasAsset = Boolean(operation.amount); - const isGrouped = metadata?.operationCount && metadata.operationCount > 1; - const amount = (metadata?.totalAmount || operation.amount || '0').toString(); - const depth = operation.depth || 0; + const amount = metadata?.amount; + const assetId = metadata?.assetId; + const hasAsset = Boolean(amount && assetId); + const depth = metadata?.depth || 0; return ( - + - {operation.isRoot && ( - - root + + + Contract Call + + + + {hasAsset && amount && assetId && ( + + + )} - - {isGrouped && ( - - - This contract call occurs {metadata.operationCount} times - - + {metadata?.functionName && ( + Function: {metadata.functionName} )} - - - - - {hasAsset ? ( - - ) : ( - - Contract interaction - - )} - ); @@ -80,40 +48,28 @@ const styles = { padding: '$2', backgroundColor: '$cardBg', borderRadius: '$md', - }), - iconCol: cssObj({ - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - width: '24px', - flexShrink: 0, + width: '100%', + minWidth: 0, // Prevent flex items from overflowing }), contentCol: cssObj({ display: 'flex', flex: 1, + minWidth: 0, // Allow content to shrink if needed }), - line: cssObj({ + header: cssObj({ display: 'flex', - alignItems: 'flex-start', + alignItems: 'center', gap: '$2', }), - alert: cssObj({ + typeLabel: cssObj({ + color: '$gray8', backgroundColor: '$gray3', - border: 'none', - padding: '$1', + padding: '$1 $2', + borderRadius: '$md', + fontWeight: '$normal', }), - rootTag: cssObj({ - backgroundColor: '$gray3', - padding: '0 $1', - borderRadius: '$xs', - alignSelf: 'flex-start', + functionName: cssObj({ + fontSize: '$sm', + color: '$gray8', }), - depthIndicator: (depth: number) => - cssObj({ - width: depth ? '2px' : '0', - minWidth: depth ? '2px' : '0', - backgroundColor: '$gray4', - marginLeft: `${depth * 8}px`, - alignSelf: 'stretch', - }), }; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationNesting.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationNesting.tsx new file mode 100644 index 0000000000..84dfdbbdc2 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationNesting.tsx @@ -0,0 +1,32 @@ +import { cssObj } from '@fuel-ui/css'; +import { Box } from '@fuel-ui/react'; + +type TxOperationNestingProps = { + depth: number; +}; + +export function TxOperationNesting({ depth }: TxOperationNestingProps) { + if (depth === 0) return null; + + return ( + + {Array.from({ length: depth }).map((_, _i) => ( + + ))} + + ); +} + +const styles = { + root: cssObj({ + display: 'flex', + alignItems: 'center', + gap: '$1', + marginRight: '$2', + }), + line: cssObj({ + width: '12px', + height: '100%', + borderLeft: '1px solid $gray4', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx index ec6ae96f8f..c49204a4e7 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx @@ -1,38 +1,48 @@ import { cssObj } from '@fuel-ui/css'; -import { Box, Text } from '@fuel-ui/react'; +import { Box, Icon, Text } from '@fuel-ui/react'; +import { ReceiptType } from 'fuels'; import type { SimplifiedOperation } from '../../../../types'; import { TxAddressDisplay } from './TxAddressDisplay'; import { TxAssetDisplay } from './TxAssetDisplay'; +import { TxOperationNesting } from './TxOperationNesting'; type TxOperationTransferProps = { operation: SimplifiedOperation; }; export function TxOperationTransfer({ operation }: TxOperationTransferProps) { - const amount = ( - operation.metadata?.totalAmount || - operation.amount || - '0' - ).toString(); - const operationCount = operation.metadata?.operationCount; - const depth = operation.depth || 0; + const depth = operation.metadata?.depth || 0; + const receiptType = operation.metadata?.receiptType; return ( - - - {operation.isRoot && ( - - root + + + + + Transfer - )} - - - + {receiptType && ( + + {receiptType} + + )} + + {/* From address */} + + {/* Asset amount with arrow */} + + + + + + + {/* To address */} + ); @@ -43,26 +53,44 @@ const styles = { display: 'flex', alignItems: 'flex-start', gap: '$2', - padding: '$2', + padding: '$3', backgroundColor: '$cardBg', borderRadius: '$md', + width: '100%', + minWidth: 0, // Prevent flex items from overflowing }), contentCol: cssObj({ display: 'flex', flex: 1, + minWidth: 0, // Allow content to shrink if needed }), - rootTag: cssObj({ + header: cssObj({ + display: 'flex', + alignItems: 'center', + gap: '$2', + }), + typeLabel: cssObj({ + color: '$gray8', backgroundColor: '$gray3', - padding: '0 $1', - borderRadius: '$xs', - alignSelf: 'flex-start', + padding: '$1 $2', + borderRadius: '$md', + fontWeight: '$normal', + }), + receiptLabel: cssObj({ + color: '$blue9', + backgroundColor: '$blue3', + padding: '$1 $2', + borderRadius: '$md', + fontWeight: '$normal', + }), + amountRow: cssObj({ + display: 'flex', + alignItems: 'flex-start', + gap: '$2', + marginLeft: '$2', + }), + arrow: cssObj({ + color: '$gray8', + marginTop: '$1', }), - depthIndicator: (depth: number) => - cssObj({ - width: depth ? '2px' : '0', - minWidth: depth ? '2px' : '0', - backgroundColor: '$gray4', - marginLeft: `${depth * 8}px`, - alignSelf: 'stretch', - }), }; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx index 59d3164afe..5c47931835 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx @@ -11,13 +11,10 @@ export function TxViewSimple({ isLoading, footer, }: SimplifiedTransactionViewProps) { + console.log('Rapid fire', transaction); return ( - + {showDetails && ( diff --git a/packages/app/src/systems/Transaction/types.ts b/packages/app/src/systems/Transaction/types.ts new file mode 100644 index 0000000000..817f3cd11b --- /dev/null +++ b/packages/app/src/systems/Transaction/types.ts @@ -0,0 +1,140 @@ +import type { + AddressType, + BN, + CallResult, + CoinTransactionRequestInput, + CoinTransactionRequestOutput, + InputContract, + OutputContract, + OutputContractCreated, + Receipt, + ReceiptType, + TransactionRequest, + TransactionRequestInput, + TransactionRequestLike, + TransactionStatus, + TransactionSummary, +} from 'fuels'; +import type { OperationFunctionCall } from 'fuels'; +import type { ReactNode } from 'react'; + +export enum TxCategory { + SEND = 'send', + RECEIVE = 'receive', + CONTRACTCALL = 'contractcall', + SCRIPT = 'script', + PREDICATE = 'predicate', +} + +export type TxRecipientAddress = { + address: string; + type: AddressType; +}; + +export type TxRequest = TransactionRequestLike; +export type TxSimulateResult = CallResult; +export type TxInput = TransactionRequestInput; +export type TxInputCoin = CoinTransactionRequestInput; +export type TxInputContract = InputContract; +export type TxOutputCoin = CoinTransactionRequestOutput; +export type TxOutputContract = OutputContract; +export type TxOutputContractCreated = OutputContractCreated; + +export type TransactionCursor = { + address: string; + size: number; + providerUrl: string; + endCursor: string; +}; + +export enum OperationDirection { + to = 'To', + from = 'From', + unknown = 'Unknown', +} + +export type ContractCallMetadata = { + contractId?: string; + functionName?: string; + functionData?: OperationFunctionCall; + amount?: BN; + assetId?: string; + operationCount?: number; + depth?: number; + receiptType?: ReceiptType; +}; + +export type SwapMetadata = { + isSwap: boolean; + receiveAmount: string; + receiveAssetId: string; + totalAmount?: BN; + operationCount?: number; + receipts?: Receipt[]; + depth?: number; + parentReceiptId?: string; +}; + +export interface SimplifiedOperation { + type: TxCategory; + from: string; + to: string; + amount?: BN; + assetId?: string; + isFromCurrentAccount: boolean; + metadata?: ContractCallMetadata; +} + +export type SimplifiedFee = { + total: BN; + network: BN; + tip?: BN; + gasUsed?: BN; + gasPrice?: BN; +}; + +export type SimplifiedTransaction = { + id?: string; + operations: SimplifiedOperation[]; + timestamp?: Date; + fee: SimplifiedFee; + origin?: { + name: string; + favicon?: string; + url?: string; + }; + original: { + summary: TransactionSummary; + request?: TransactionRequest; + }; +}; + +export type SimplifiedTransactionViewProps = { + transaction: SimplifiedTransaction; + showDetails?: boolean; + isLoading?: boolean; + footer?: ReactNode; +}; + +export interface AssetFlow { + assetId: string; + amount: BN; + from: string; + to: string; + type: 'in' | 'out'; // from perspective of current user +} + +export interface SimplifiedAssetFlows { + assetsIn: AssetFlow[]; + assetsOut: AssetFlow[]; + fees: { + gasUsed: BN; + networkFee: BN; + tip: BN; + otherFees: AssetFlow[]; // Other fees paid in various assets + }; + contractInteractions: Array<{ + contractId: string; + functionName?: string; + }>; +} diff --git a/packages/app/src/systems/Transaction/types.tsx b/packages/app/src/systems/Transaction/types.tsx index ae1e5b5af8..c4b3e7b690 100644 --- a/packages/app/src/systems/Transaction/types.tsx +++ b/packages/app/src/systems/Transaction/types.tsx @@ -58,13 +58,7 @@ export type ContractCallMetadata = { functionData?: OperationFunctionCall; amount?: BN; assetId?: string; - isContractCallGroup?: boolean; operationCount?: number; - totalAmount?: BN; - isRoot?: boolean; - receipts?: Receipt[]; - depth?: number; - parentReceiptId?: string; }; export type SwapMetadata = { @@ -78,18 +72,15 @@ export type SwapMetadata = { parentReceiptId?: string; }; -export type SimplifiedOperation = { +export interface SimplifiedOperation { type: TxCategory; from: string; to: string; amount?: BN; assetId?: string; - isFromCurrentAccount?: boolean; - isRoot?: boolean; - groupId?: string; - depth?: number; - metadata?: ContractCallMetadata | SwapMetadata; -}; + isFromCurrentAccount: boolean; + metadata?: ContractCallMetadata; +} export type SimplifiedFee = { total: BN; diff --git a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts index f2370b549b..d4d61879c1 100644 --- a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts +++ b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts @@ -1,15 +1,14 @@ import type { Operation, TransactionRequest, TransactionSummary } from 'fuels'; import { + BN, type OperationFunctionCall, OperationName, ReceiptType, type TransactionResultReceipt, - TransactionStatus, - bn, } from 'fuels'; import type { ContractCallMetadata, SimplifiedOperation } from '../types'; import { TxCategory } from '../types'; -import type { SimplifiedFee, SimplifiedTransaction } from '../types.tsx'; +import type { SimplifiedTransaction } from '../types.tsx'; type TransactionRequestWithOrigin = TransactionRequest & { origin?: string; @@ -31,24 +30,32 @@ function getOperationType(operation: Operation): TxCategory { } } -function isRootOperation(_operation: Operation): boolean { - return false; -} - function getReceiptDepth( receipt: TransactionResultReceipt, allReceipts: TransactionResultReceipt[] ): number { - // For now, use a simple depth calculation based on receipt order - // We can enhance this later when we have better receipt hierarchy info - const index = allReceipts.findIndex((r) => r === receipt); - return index > 0 ? 1 : 0; + const receiptIndex = allReceipts.findIndex((r) => r === receipt); + if (receiptIndex === -1) return 0; + + let depth = 0; + for (let i = 0; i < receiptIndex; i++) { + const r = allReceipts[i]; + if (r.type === ReceiptType.Call) depth++; + if (r.type === ReceiptType.ReturnData && depth > 0) depth--; + if ( + r.type === ReceiptType.ScriptResult && + allReceipts[i - 1]?.type !== ReceiptType.Return + ) + depth = 0; + } + + return depth; } function transformOperation( operation: Operation, - currentAccount?: string, - allReceipts: TransactionResultReceipt[] = [] + allReceipts: TransactionResultReceipt[], + currentAccount?: string ): SimplifiedOperation { const { name, @@ -59,12 +66,8 @@ function transformOperation( receipts = [], } = operation; const type = getOperationType(operation); - const isRoot = isRootOperation(operation); - - // Calculate depth based on receipts - const depth = receipts.length - ? Math.min(...receipts.map((r) => getReceiptDepth(r, allReceipts))) - : 0; + const receipt = receipts[0]; + const depth = receipt ? getReceiptDepth(receipt, allReceipts) : 0; const isFromCurrentAccount = currentAccount ? from?.address === currentAccount @@ -76,21 +79,17 @@ function transformOperation( contractId: to?.address, functionName: call.functionName, functionData: call, - amount: call.amount ? bn(call.amount) : undefined, + amount: call.amount ? new BN(call.amount) : undefined, assetId: call.assetId, - isRoot, - receipts, depth, + receiptType: receipt?.type, }; return { type, - groupId: `${type}-${to?.address}`, from: from?.address || '', to: to?.address || '', isFromCurrentAccount, - isRoot, - depth, metadata, }; } @@ -99,32 +98,26 @@ function transformOperation( const asset = assetsSent[0]; return { type, - groupId: `${type}-${asset.assetId}`, from: from?.address || '', to: to?.address || '', - amount: asset.amount ? bn(asset.amount) : undefined, + amount: asset.amount ? new BN(asset.amount) : undefined, assetId: asset.assetId, isFromCurrentAccount, - isRoot, - depth, metadata: { - receipts, depth, + receiptType: receipt?.type, }, }; } return { type, - groupId: `${type}-${to?.address}`, from: from?.address || '', to: to?.address || '', isFromCurrentAccount, - isRoot, - depth, metadata: { - receipts, depth, + receiptType: receipt?.type, }, }; } @@ -135,112 +128,23 @@ export function transformOperations( ): SimplifiedOperation[] { if (!summary.operations) return []; - // Get all receipts from all operations for depth calculation + // Get all receipts from all operations const allReceipts = summary.operations.flatMap((op) => op.receipts || []); - // Transform all operations but only keep ones relevant to current account - const operations = summary.operations - .filter((op) => { - if (!currentAccount) return true; - const currentAccountLower = currentAccount.toLowerCase(); - - // Check operation addresses - const opTo = op.to?.address?.toLowerCase(); - const opFrom = op.from?.address?.toLowerCase(); - const isOperationRelevant = - opTo === currentAccountLower || opFrom === currentAccountLower; - - // Check receipt addresses - const hasRelevantReceipt = op.receipts?.some((receipt) => { - // Only check transfer receipts for now as they have from/to fields - if ( - receipt.type === ReceiptType.Transfer || - receipt.type === ReceiptType.TransferOut - ) { - const transfer = receipt as { to?: string; from?: string }; - return ( - transfer.to?.toLowerCase() === currentAccountLower || - transfer.from?.toLowerCase() === currentAccountLower - ); - } - return false; - }); - - // Show operation if either the operation itself or any of its receipts involve the current account - return isOperationRelevant || hasRelevantReceipt; - }) - .map((op) => { - const transformed = transformOperation(op, currentAccount, allReceipts); - console.log('Operation:', { - type: op.name, - receipts: op.receipts?.map((r) => r.type), - isRoot: transformed.isRoot, - depth: transformed.depth, - from: op.from?.address, - to: op.to?.address, - receiptTypes: op.receipts?.map((r) => r.type), - }); - return transformed; - }); + console.log('All receipts:', allReceipts); - // Sort operations by depth - return operations.sort((a, b) => (a.depth || 0) - (b.depth || 0)); -} - -function getGroupKey(op: SimplifiedOperation): string { - const base = `${op.type}-${op.to}`; - if (op.type === TxCategory.CONTRACTCALL) return base; - return op.assetId ? `${base}-${op.assetId}` : base; -} - -export function groupSimilarOperations( - operations: SimplifiedOperation[] -): SimplifiedOperation[] { - const groups = operations.reduce( - (acc, op) => { - const key = getGroupKey(op); - if (!acc[key]) { - acc[key] = []; - } - acc[key].push(op); - return acc; - }, - {} as Record + // Transform operations with receipt depth information + const operations = summary.operations.map((op) => + transformOperation(op, allReceipts, currentAccount) ); - return Object.values(groups).map((group) => { - if (group.length === 1) return group[0]; - - const firstOp = group[0]; - const metadata = firstOp.metadata as ContractCallMetadata; - - if (firstOp.type === TxCategory.CONTRACTCALL) { - return { - ...firstOp, - metadata: { - ...metadata, - isContractCallGroup: true, - operationCount: group.length, - totalAmount: group.reduce((sum, op) => { - const opMetadata = op.metadata as ContractCallMetadata; - return opMetadata?.amount ? sum.add(opMetadata.amount) : sum; - }, bn(0)), - }, - }; - } + // Sort by depth to maintain visual hierarchy + operations.sort( + (a, b) => (a.metadata?.depth || 0) - (b.metadata?.depth || 0) + ); - return { - ...firstOp, - metadata: { - operationCount: group.length, - totalAmount: group.every( - (op) => op.amount && op.assetId === firstOp.assetId - ) - ? group.reduce((sum, op) => sum.add(op.amount!), bn(0)) - : undefined, - }, - }; - }); + console.log('Transformed operations with depth:', operations); + return operations; } export function simplifyTransaction( @@ -250,7 +154,6 @@ export function simplifyTransaction( ): SimplifiedTransaction { console.log('summary', summary); const operations = transformOperations(summary, currentAccount); - const groupedOperations = groupSimilarOperations(operations); const requestWithOrigin = request as TransactionRequestWithOrigin; const origin = requestWithOrigin?.origin; @@ -258,14 +161,14 @@ export function simplifyTransaction( return { id: summary.id, - operations: groupedOperations, + operations, timestamp: summary.time ? new Date(summary.time) : undefined, fee: { - total: bn(summary.fee || 0), - network: bn(summary.fee || 0).sub(bn(request?.tip || 0)), - tip: bn(request?.tip || 0), - gasUsed: bn(summary.gasUsed || 0), - gasPrice: bn(0), // This will be calculated later when we have access to the provider + total: new BN(summary.fee || 0), + network: new BN(summary.fee || 0).sub(new BN(request?.tip || 0)), + tip: new BN(request?.tip || 0), + gasUsed: new BN(summary.gasUsed || 0), + gasPrice: new BN(0), }, origin: origin ? { From 36cf37e718a51986f9b2e61166c4793554ed8583 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Thu, 23 Jan 2025 16:32:31 -0300 Subject: [PATCH 22/85] delete unused components --- .../TxOperationsSimple/TxOperationSend.tsx | 106 ------------------ .../operations/TxOperationContract.tsx | 75 ------------- .../operations/TxOperationSwap.tsx | 59 ---------- .../operations/TxOperationTransfer.tsx | 96 ---------------- 4 files changed, 336 deletions(-) delete mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSend.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationSwap.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSend.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSend.tsx deleted file mode 100644 index a89a941d37..0000000000 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationSend.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { Avatar, Box, Icon, IconButton, Text } from '@fuel-ui/react'; -import { useAccounts } from '~/systems/Account'; -import { formatAmount, shortAddress } from '~/systems/Core'; -import type { SimplifiedOperation } from '../../../types'; -import { TxCategory } from '../../../types'; - -type TxOperationSendProps = { - operation: SimplifiedOperation; -}; - -export function TxOperationSend({ operation }: TxOperationSendProps) { - const { accounts } = useAccounts(); - const amount = operation.metadata?.totalAmount || operation.amount || '0'; - const operationCount = operation.metadata?.operationCount; - const isContractCall = operation.type === TxCategory.CONTRACTCALL; - - // Find if addresses match our accounts - const fromAccount = accounts?.find( - (acc) => acc.address.toLowerCase() === operation.from.toLowerCase() - ); - const toAccount = accounts?.find( - (acc) => acc.address.toLowerCase() === operation.to.toLowerCase() - ); - - return ( - - - - - - - - {fromAccount ? fromAccount.name : shortAddress(operation.from)} - - navigator.clipboard.writeText(operation.from)} - /> - - - {isContractCall && !toAccount && ( - - Calls contract (sending tokens) - - )} - - - - {formatAmount({ - amount, - options: { units: 9 }, - })}{' '} - {shortAddress(operation.assetId)} - - {operationCount && operationCount > 1 ? ( - - x{operationCount} - - ) : null} - - - - {toAccount ? toAccount.name : shortAddress(operation.to)} - - navigator.clipboard.writeText(operation.to)} - /> - - - - - ); -} - -const styles = { - line: cssObj({ - display: 'flex', - alignItems: 'flex-start', - gap: '$3', - }), - iconCol: cssObj({ - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - width: '24px', - flexShrink: 0, - }), - contentCol: cssObj({ - display: 'flex', - flex: 1, - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx deleted file mode 100644 index 0ca89bcacd..0000000000 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationContract.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { Box, Text } from '@fuel-ui/react'; -import type { SimplifiedOperation } from '../../../../types'; -import { TxAddressDisplay } from './TxAddressDisplay'; -import { TxAssetDisplay } from './TxAssetDisplay'; -import { TxOperationNesting } from './TxOperationNesting'; - -type TxOperationContractProps = { - operation: SimplifiedOperation; -}; - -export function TxOperationContract({ operation }: TxOperationContractProps) { - const metadata = operation.metadata; - const amount = metadata?.amount; - const assetId = metadata?.assetId; - const hasAsset = Boolean(amount && assetId); - const depth = metadata?.depth || 0; - - return ( - - - - - - Contract Call - - - - - {hasAsset && amount && assetId && ( - - - - )} - {metadata?.functionName && ( - Function: {metadata.functionName} - )} - - - ); -} - -const styles = { - root: cssObj({ - display: 'flex', - alignItems: 'flex-start', - gap: '$2', - padding: '$2', - backgroundColor: '$cardBg', - borderRadius: '$md', - width: '100%', - minWidth: 0, // Prevent flex items from overflowing - }), - contentCol: cssObj({ - display: 'flex', - flex: 1, - minWidth: 0, // Allow content to shrink if needed - }), - header: cssObj({ - display: 'flex', - alignItems: 'center', - gap: '$2', - }), - typeLabel: cssObj({ - color: '$gray8', - backgroundColor: '$gray3', - padding: '$1 $2', - borderRadius: '$md', - fontWeight: '$normal', - }), - functionName: cssObj({ - fontSize: '$sm', - color: '$gray8', - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationSwap.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationSwap.tsx deleted file mode 100644 index 3bd6393588..0000000000 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationSwap.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { Box, Icon } from '@fuel-ui/react'; -import type { SimplifiedOperation } from '../../../../types'; -import { useEcosystemProject } from '../useEcosystemProject'; -import { isSwapMetadata } from '../utils'; -import { TxAddressDisplay } from './TxAddressDisplay'; -import { TxAssetDisplay } from './TxAssetDisplay'; - -type TxOperationSwapProps = { - operation: SimplifiedOperation; -}; - -export function TxOperationSwap({ operation }: TxOperationSwapProps) { - const { - name: projectName, - image: projectImage, - isLoading, - } = useEcosystemProject(operation.to); - - if (!isSwapMetadata(operation.metadata)) return null; - - return ( - <> - - - - - - - - - ); -} - -const styles = { - line: cssObj({ - display: 'flex', - alignItems: 'flex-start', - gap: '$3', - }), - iconCol: cssObj({ - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - width: '24px', - flexShrink: 0, - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx deleted file mode 100644 index c49204a4e7..0000000000 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationTransfer.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { Box, Icon, Text } from '@fuel-ui/react'; -import { ReceiptType } from 'fuels'; -import type { SimplifiedOperation } from '../../../../types'; -import { TxAddressDisplay } from './TxAddressDisplay'; -import { TxAssetDisplay } from './TxAssetDisplay'; -import { TxOperationNesting } from './TxOperationNesting'; - -type TxOperationTransferProps = { - operation: SimplifiedOperation; -}; - -export function TxOperationTransfer({ operation }: TxOperationTransferProps) { - const depth = operation.metadata?.depth || 0; - const receiptType = operation.metadata?.receiptType; - - return ( - - - - - - Transfer - - {receiptType && ( - - {receiptType} - - )} - - {/* From address */} - - {/* Asset amount with arrow */} - - - - - - - {/* To address */} - - - - ); -} - -const styles = { - root: cssObj({ - display: 'flex', - alignItems: 'flex-start', - gap: '$2', - padding: '$3', - backgroundColor: '$cardBg', - borderRadius: '$md', - width: '100%', - minWidth: 0, // Prevent flex items from overflowing - }), - contentCol: cssObj({ - display: 'flex', - flex: 1, - minWidth: 0, // Allow content to shrink if needed - }), - header: cssObj({ - display: 'flex', - alignItems: 'center', - gap: '$2', - }), - typeLabel: cssObj({ - color: '$gray8', - backgroundColor: '$gray3', - padding: '$1 $2', - borderRadius: '$md', - fontWeight: '$normal', - }), - receiptLabel: cssObj({ - color: '$blue9', - backgroundColor: '$blue3', - padding: '$1 $2', - borderRadius: '$md', - fontWeight: '$normal', - }), - amountRow: cssObj({ - display: 'flex', - alignItems: 'flex-start', - gap: '$2', - marginLeft: '$2', - }), - arrow: cssObj({ - color: '$gray8', - marginTop: '$1', - }), -}; From 22f33d554763cffcefd2d6eff9741a0a41309e88 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Thu, 23 Jan 2025 16:33:34 -0300 Subject: [PATCH 23/85] simplify --- .../TxOperationsSimple/TxOperationDrawer.tsx | 2 +- .../TxOperationsSimple/TxOperationsList.tsx | 11 +- .../TxViewSimple/TxOperationsSimple/index.ts | 1 - .../operations/TxOperation.tsx | 110 ++++++++++++++++++ 4 files changed, 113 insertions(+), 11 deletions(-) create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationDrawer.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationDrawer.tsx index 295550a9f7..1ad26bb471 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationDrawer.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationDrawer.tsx @@ -39,7 +39,7 @@ export function TxOperationDrawer({ Function: {operation.metadata.functionName} )} - Contract: {operation.to} + {operation.to} {operation.metadata?.operationCount && ( Total Calls: {operation.metadata.operationCount} diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx index 0c85a871af..7ba885b060 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx @@ -1,9 +1,7 @@ import { Box } from '@fuel-ui/react'; import { useMemo } from 'react'; import type { SimplifiedOperation } from '../../../types'; -import { TxCategory } from '../../../types'; -import { TxOperationContract } from './operations/TxOperationContract'; -import { TxOperationTransfer } from './operations/TxOperationTransfer'; +import { TxOperation } from './operations/TxOperation'; type TxOperationsListProps = { operations: SimplifiedOperation[]; @@ -14,12 +12,7 @@ export function TxOperationsList({ operations }: TxOperationsListProps) { return operations.map((operation, index) => { console.log('Rendering operation:', operation); const key = `${operation.type}-${operation.from}-${operation.to}-${index}`; - - if (operation.type === TxCategory.SEND) { - return ; - } - - return ; + return ; }); }, [operations]); diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/index.ts b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/index.ts index ecb55f59a5..3e30024286 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/index.ts +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/index.ts @@ -1,4 +1,3 @@ export * from './TxOperationDrawer'; export * from './TxOperationHeader'; -export * from './TxOperationSend'; export * from './utils'; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx new file mode 100644 index 0000000000..e1d414e6b1 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx @@ -0,0 +1,110 @@ +import { cssObj } from '@fuel-ui/css'; +import { Box, Icon, Text } from '@fuel-ui/react'; +import { ReceiptType } from 'fuels'; +import { TxCategory } from '../../../../types'; +import type { SimplifiedOperation } from '../../../../types'; +import { TxAddressDisplay } from './TxAddressDisplay'; +import { TxAssetDisplay } from './TxAssetDisplay'; +import { TxOperationNesting } from './TxOperationNesting'; + +type TxOperationProps = { + operation: SimplifiedOperation; +}; + +export function TxOperation({ operation }: TxOperationProps) { + const metadata = operation.metadata; + const amount = metadata?.amount || operation.amount; + const assetId = metadata?.assetId || operation.assetId; + const hasAsset = Boolean(amount && assetId); + const depth = metadata?.depth || 0; + const receiptType = metadata?.receiptType; + const isContract = operation.type === TxCategory.CONTRACTCALL; + + const receiptTypeLabel = receiptType ? ReceiptType[receiptType] : null; + + if (depth !== 0) return null; + + return ( + + + + + + {isContract ? 'Contract Call' : 'Transfer'} + + {receiptType && ( + + {receiptTypeLabel} + + )} + + + + {hasAsset && amount && assetId && ( + + {!isContract && } + + + )} + {metadata?.functionName && ( + Function: {metadata.functionName} + )} + + + ); +} +// root level rule is only for contract calls, all transfers should show as well +// we also have the operations not related to the account in a group, and intermediate contract calls +const styles = { + root: cssObj({ + display: 'flex', + alignItems: 'flex-start', + gap: '$2', + padding: '$3', + backgroundColor: '$cardBg', + borderRadius: '$md', + width: '100%', + minWidth: 0, + }), + contentCol: cssObj({ + display: 'flex', + flex: 1, + minWidth: 0, + }), + header: cssObj({ + display: 'flex', + alignItems: 'center', + gap: '$2', + }), + typeLabel: cssObj({ + color: '$gray8', + backgroundColor: '$gray3', + padding: '$1 $2', + borderRadius: '$md', + fontWeight: '$normal', + }), + receiptLabel: cssObj({ + color: '$blue9', + backgroundColor: '$blue3', + padding: '$1 $2', + borderRadius: '$md', + fontWeight: '$normal', + }), + arrow: cssObj({ + color: '$gray8', + marginTop: '$1', + marginLeft: '$2', + }), + functionName: cssObj({ + fontSize: '$sm', + color: '$gray8', + }), +}; From 9b9d6fba83586527153ae968bff17ce49950e1cd Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Fri, 24 Jan 2025 12:34:49 -0300 Subject: [PATCH 24/85] styles: begin figma work --- .../components/TxOperations/TxOperations.tsx | 4 +- .../TxOperationsSimple/TxOperationsGroup.tsx | 90 +++++++++++++++++++ .../TxOperationsSimple/TxOperationsList.tsx | 82 +++++++++++++++-- .../operations/TxAddressDisplay.tsx | 21 ++--- .../operations/TxOperation.tsx | 52 ++++++----- 5 files changed, 205 insertions(+), 44 deletions(-) create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx diff --git a/packages/app/src/systems/Transaction/components/TxOperations/TxOperations.tsx b/packages/app/src/systems/Transaction/components/TxOperations/TxOperations.tsx index 0e8d253837..28200e7d6a 100644 --- a/packages/app/src/systems/Transaction/components/TxOperations/TxOperations.tsx +++ b/packages/app/src/systems/Transaction/components/TxOperations/TxOperations.tsx @@ -28,7 +28,7 @@ export function TxOperations({ } return ( - + {operations?.map((operation, index) => ( @@ -43,7 +43,7 @@ export function TxOperations({ } TxOperations.Loader = () => ( - + ); diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx new file mode 100644 index 0000000000..f37c54422d --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx @@ -0,0 +1,90 @@ +import { cssObj } from '@fuel-ui/css'; +import { Box, Icon, Text } from '@fuel-ui/react'; +import { useState } from 'react'; +import type { SimplifiedOperation } from '../../../types'; +import { TxOperation } from './operations/TxOperation'; + +type TxOperationsGroupProps = { + title: string; + operations: SimplifiedOperation[]; + showNesting?: boolean; +}; + +export function TxOperationsGroup({ + title, + operations, + showNesting, +}: TxOperationsGroupProps) { + const [isExpanded, setIsExpanded] = useState(false); + + if (!operations.length) return null; + + const handleClick = (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsExpanded(!isExpanded); + }; + + return ( + + + + + {title} ({operations.length}) + + + {isExpanded && ( + e.stopPropagation()} + > + {operations.map((operation, index) => ( + + ))} + + )} + + ); +} + +const styles = { + root: cssObj({ + borderTop: '1px solid $border', + marginTop: '$2', + paddingTop: '$2', + }), + header: cssObj({ + display: 'flex', + alignItems: 'center', + gap: '$2', + padding: '$2', + cursor: 'pointer', + backgroundColor: 'transparent', + border: 'none', + width: '100%', + borderRadius: '$md', + transition: 'all 0.2s ease', + '&:hover': { + backgroundColor: '$gray3', + }, + }), + icon: cssObj({ + color: '$gray8', + }), + title: cssObj({ + color: '$gray8', + fontWeight: '$medium', + }), + content: cssObj({ + paddingLeft: '$6', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx index 7ba885b060..440c82ce82 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx @@ -1,22 +1,86 @@ import { Box } from '@fuel-ui/react'; import { useMemo } from 'react'; import type { SimplifiedOperation } from '../../../types'; +import { TxCategory } from '../../../types'; +import { TxOperationsGroup } from './TxOperationsGroup'; import { TxOperation } from './operations/TxOperation'; type TxOperationsListProps = { operations: SimplifiedOperation[]; + currentAccount?: string; }; -export function TxOperationsList({ operations }: TxOperationsListProps) { - const renderedOperations = useMemo(() => { - return operations.map((operation, index) => { - console.log('Rendering operation:', operation); - const key = `${operation.type}-${operation.from}-${operation.to}-${index}`; - return ; - }); - }, [operations]); +export function TxOperationsList({ + operations, + currentAccount, +}: TxOperationsListProps) { + const { mainOperations, otherRootOperations, intermediateOperations } = + useMemo(() => { + const main: SimplifiedOperation[] = []; + const otherRoot: SimplifiedOperation[] = []; + const intermediate: SimplifiedOperation[] = []; - return {renderedOperations}; + for (const op of operations) { + const depth = op.metadata?.depth || 0; + const isTransfer = op.type === TxCategory.SEND; + const isFromCurrentAccount = + currentAccount && op.from === currentAccount; + const isToCurrentAccount = currentAccount && op.to === currentAccount; + + // All transfers go to main list + if (isTransfer) { + main.push(op); + continue; + } + + // Contract calls at root level (depth 0) + if (depth === 0) { + // If related to current account, show in main list + if (isFromCurrentAccount || isToCurrentAccount) { + main.push(op); + } else { + otherRoot.push(op); + } + continue; + } + + // All other operations (intermediate contract calls) + intermediate.push(op); + } + + return { + mainOperations: main, + otherRootOperations: otherRoot, + intermediateOperations: intermediate, + }; + }, [operations, currentAccount]); + + return ( + + {/* Main operations (transfers and root contract calls related to current account) */} + {mainOperations.map((operation, index) => ( + + ))} + + {/* Other root operations not related to current account */} + + + {/* Intermediate operations with nesting */} + + + ); } TxOperationsList.Loader = function TxOperationsListLoader() { diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx index 8180c466be..d632187167 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx @@ -16,7 +16,6 @@ export function TxAddressDisplay({ name, image, isContract, - label, }: TxAddressDisplayProps) { const { accounts } = useAccounts(); const account = accounts?.find( @@ -27,19 +26,19 @@ export function TxAddressDisplay({ {image ? ( - + ) : ( - + )} - {label && ( - - {label}: - - )} - {account?.name || name || shortAddress(address)} + {account?.name || 'Unknown'} {isContract && ( @@ -48,6 +47,9 @@ export function TxAddressDisplay({ )} + + {shortAddress(address)} + - + {isContract && showNesting && } @@ -38,19 +48,17 @@ export function TxOperation({ operation }: TxOperationProps) { )} - - + + {isTransfer && ( + + )} + {hasAsset && amount && assetId && ( - {!isContract && } )} @@ -61,23 +69,23 @@ export function TxOperation({ operation }: TxOperationProps) { ); } -// root level rule is only for contract calls, all transfers should show as well // we also have the operations not related to the account in a group, and intermediate contract calls const styles = { root: cssObj({ - display: 'flex', - alignItems: 'flex-start', - gap: '$2', - padding: '$3', - backgroundColor: '$cardBg', - borderRadius: '$md', + padding: '$1', + backgroundColor: '#E0E0E0', + borderRadius: '12px', width: '100%', - minWidth: 0, + boxSizing: 'border-box', }), contentCol: cssObj({ display: 'flex', + backgroundColor: 'white', + boxShadow: '0px 2px 6px -1px #2020201A, 0px 0px 0px 1px #2020201F', flex: 1, + borderRadius: '8px', minWidth: 0, + padding: '$3', }), header: cssObj({ display: 'flex', @@ -99,9 +107,7 @@ const styles = { fontWeight: '$normal', }), arrow: cssObj({ - color: '$gray8', - marginTop: '$1', - marginLeft: '$2', + color: '#0D74CE', }), functionName: cssObj({ fontSize: '$sm', From 0c3cbc0ca382dcbbfe452b534cfb9ef67edf1c2d Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Fri, 24 Jan 2025 17:20:25 -0300 Subject: [PATCH 25/85] style: make it a grid --- .../operations/TxAddressDisplay.tsx | 12 +- .../operations/TxOperation.tsx | 192 ++++++++++++++---- 2 files changed, 157 insertions(+), 47 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx index d632187167..21c0220c8b 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx @@ -37,7 +37,7 @@ export function TxAddressDisplay({ )} - + {account?.name || 'Unknown'} {isContract && ( @@ -47,7 +47,7 @@ export function TxAddressDisplay({ )} - + {shortAddress(address)} { + return ; +}; + export function TxOperation({ operation, showNesting = true, @@ -27,8 +40,20 @@ export function TxOperation({ const isContract = operation.type === TxCategory.CONTRACTCALL; const isTransfer = operation.type === TxCategory.SEND; const _contract = useContractMetadata(operation.from); + const { accounts } = useAccounts(); + const accountFrom = accounts?.find( + (acc) => acc.address.toLowerCase() === operation.from.toLowerCase() + ); + const accountTo = accounts?.find( + (acc) => acc.address.toLowerCase() === operation.to.toLowerCase() + ); + const _receiptTypeLabel = receiptType ? ReceiptType[receiptType] : null; - const receiptTypeLabel = receiptType ? ReceiptType[receiptType] : null; + const getOperationType = () => { + if (isContract) return 'Calls contract (sending funds)'; + if (isTransfer) return 'Sends token'; + return 'Unknown'; + }; // For transfers, always show with 0 indentation // For contract calls, only show if root level (depth === 0) unless showNesting is true @@ -36,35 +61,100 @@ export function TxOperation({ return ( - {isContract && showNesting && } - - - {isContract ? 'Contract Call' : 'Transfer'} - - {receiptType && ( - - {receiptTypeLabel} + + + + + + + {accountFrom?.name || 'Unknown'} - )} - - - {isTransfer && ( - - )} - - {hasAsset && amount && assetId && ( - - + + Contract + + + )} + + {shortAddress(operation.from)} + + navigator.clipboard.writeText(operation.from)} /> - - )} - {metadata?.functionName && ( - Function: {metadata.functionName} - )} + + + + + + + + + + {getOperationType()} + + + + + + {hasAsset && amount && assetId && ( + + )} + + + + + + + {accountTo?.name || 'Unknown'} + + {isContract && ( + + + Contract + + + )} + + {shortAddress(operation.to)} + + navigator.clipboard.writeText(operation.to)} + /> + + ); @@ -87,30 +177,42 @@ const styles = { minWidth: 0, padding: '$3', }), - header: cssObj({ + blue: cssObj({ + fontSize: '$sm', display: 'flex', alignItems: 'center', - gap: '$2', + gap: '$1', + color: '#0D74CE', }), - typeLabel: cssObj({ + functionName: cssObj({ + fontSize: '$sm', color: '$gray8', - backgroundColor: '$gray3', - padding: '$1 $2', - borderRadius: '$md', - fontWeight: '$normal', }), - receiptLabel: cssObj({ - color: '$blue9', - backgroundColor: '$blue3', - padding: '$1 $2', + spacer: cssObj({ + borderLeft: '1px solid #D9D9D9', + minHeight: '14px', + width: '2px', + height: '100%', + backgroundColor: '#D9D9D9', + borderRadius: '$lg', + }), + iconCol: cssObj({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + flexShrink: 0, + }), + badge: cssObj({ + padding: '0 $1', + backgroundColor: '$gray3', borderRadius: '$md', - fontWeight: '$normal', }), - arrow: cssObj({ - color: '#0D74CE', + name: cssObj({ + fontWeight: '$semibold', + color: '#202020', }), - functionName: cssObj({ - fontSize: '$sm', - color: '$gray8', + address: cssObj({ + fontWeight: '$medium', + color: '#646464', }), }; From 52485a3b30fb3aba4d509c362bb78a1978538ef2 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Sat, 25 Jan 2025 17:00:28 -0300 Subject: [PATCH 26/85] feat: show assets properly --- .../operations/TxOperation.tsx | 121 ++++++++++++++---- .../components/TxViewSimple/TxViewSimple.tsx | 1 - packages/app/src/systems/Transaction/types.ts | 12 +- .../Transaction/utils/simplifyTransaction.ts | 6 +- 4 files changed, 108 insertions(+), 32 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx index 05869919c2..91b5b51892 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx @@ -1,22 +1,21 @@ import { cssObj } from '@fuel-ui/css'; import { Avatar, - AvatarGenerated, Box, + ContentLoader, Icon, IconButton, Text, } from '@fuel-ui/react'; -import { ReceiptType } from 'fuels'; +import type { AssetFuelAmount } from '@fuel-wallet/types'; +import { useEffect, useState } from 'react'; import { useAccounts } from '~/systems/Account'; -import { useContractMetadata } from '~/systems/Contract/hooks/useContractMetadata'; +import { AssetsAmount } from '~/systems/Asset'; +import { AssetsCache } from '~/systems/Asset/cache/AssetsCache'; import { shortAddress } from '~/systems/Core'; +import { NetworkService } from '~/systems/Network/services/network'; import { TxCategory } from '../../../../types'; import type { SimplifiedOperation } from '../../../../types'; -import { TxRecipientContractLogo } from '../../../TxRecipientCard/TxRecipientContractLogo'; -import { TxAddressDisplay } from './TxAddressDisplay'; -import { TxAssetDisplay } from './TxAssetDisplay'; -import { TxOperationNesting } from './TxOperationNesting'; type TxOperationProps = { operation: SimplifiedOperation; @@ -27,27 +26,95 @@ const SpacerComponent = () => { return ; }; +const AssetLoader = () => ( + + + +); + export function TxOperation({ operation, showNesting = true, }: TxOperationProps) { const metadata = operation.metadata; - const amount = metadata?.amount || operation.amount; - const assetId = metadata?.assetId || operation.assetId; - const hasAsset = Boolean(amount && assetId); - const depth = metadata?.depth || 0; - const receiptType = metadata?.receiptType; const isContract = operation.type === TxCategory.CONTRACTCALL; const isTransfer = operation.type === TxCategory.SEND; - const _contract = useContractMetadata(operation.from); + const depth = metadata?.depth || 0; const { accounts } = useAccounts(); + const [assetsAmount, setAssetsAmount] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const accountFrom = accounts?.find( (acc) => acc.address.toLowerCase() === operation.from.toLowerCase() ); const accountTo = accounts?.find( (acc) => acc.address.toLowerCase() === operation.to.toLowerCase() ); - const _receiptTypeLabel = receiptType ? ReceiptType[receiptType] : null; + + useEffect(() => { + const fetchAssetsAmount = async () => { + try { + const coins = []; + if (operation.amount && operation.assetId) { + coins.push({ + amount: operation.amount, + assetId: operation.assetId, + }); + } else if (metadata?.amount && metadata?.assetId) { + coins.push({ + amount: metadata.amount, + assetId: metadata.assetId, + }); + } + + if (!coins.length) return; + + setIsLoading(true); + const assetsCache = AssetsCache.getInstance(); + const network = await NetworkService.getSelectedNetwork(); + if (!network) return; + + console.log('Fetching assets for:', coins); + + const assetsWithAmount = await Promise.all( + coins.map(async (operationCoin) => { + const assetCached = await assetsCache.getAsset({ + chainId: network.chainId, + assetId: operationCoin.assetId, + dbAssets: [], + save: false, + }); + + if (!assetCached) return null; + + return { + type: 'fuel', + chainId: network.chainId, + name: assetCached.name, + symbol: assetCached.symbol, + decimals: assetCached.decimals, + icon: assetCached.icon, + assetId: operationCoin.assetId, + amount: operationCoin.amount, + } as AssetFuelAmount; + }) + ); + + const filteredAssets = assetsWithAmount.filter( + (a): a is AssetFuelAmount => a !== null + ); + console.log('Setting assets:', filteredAssets); + setAssetsAmount(filteredAssets); + } catch (error) { + console.error('Error fetching assets:', error); + setAssetsAmount([]); + } finally { + setIsLoading(false); + } + }; + + fetchAssetsAmount(); + }, []); // Only run once when component mounts const getOperationType = () => { if (isContract) return 'Calls contract (sending funds)'; @@ -59,6 +126,10 @@ export function TxOperation({ // For contract calls, only show if root level (depth === 0) unless showNesting is true if (isContract && !showNesting && depth !== 0) return null; + const shouldShowAssetAmount = + (operation.amount && operation.assetId) || + (metadata?.amount && metadata?.assetId); + return ( @@ -72,6 +143,7 @@ export function TxOperation({ marginBottom: '$2', })} > + {/* From Address */} navigator.clipboard.writeText(operation.from)} /> + + {/* Spacer and Arrow */} - + {getOperationType()} + + {/* Asset Amount */} - {hasAsset && amount && assetId && ( - - )} + {shouldShowAssetAmount && + (isLoading ? ( + + ) : assetsAmount.length > 0 ? ( + + ) : null)} + + {/* To Address */} diff --git a/packages/app/src/systems/Transaction/types.ts b/packages/app/src/systems/Transaction/types.ts index 817f3cd11b..e6f42ca95c 100644 --- a/packages/app/src/systems/Transaction/types.ts +++ b/packages/app/src/systems/Transaction/types.ts @@ -1,3 +1,4 @@ +import type { AssetFuelAmount } from '@fuel-wallet/types'; import type { AddressType, BN, @@ -21,7 +22,7 @@ import type { ReactNode } from 'react'; export enum TxCategory { SEND = 'send', RECEIVE = 'receive', - CONTRACTCALL = 'contractcall', + CONTRACTCALL = 'contractCall', SCRIPT = 'script', PREDICATE = 'predicate', } @@ -59,9 +60,9 @@ export type ContractCallMetadata = { functionData?: OperationFunctionCall; amount?: BN; assetId?: string; - operationCount?: number; depth?: number; receiptType?: ReceiptType; + assetAmount?: AssetFuelAmount; }; export type SwapMetadata = { @@ -75,15 +76,16 @@ export type SwapMetadata = { parentReceiptId?: string; }; -export interface SimplifiedOperation { +export type SimplifiedOperation = { type: TxCategory; from: string; to: string; amount?: BN; assetId?: string; isFromCurrentAccount: boolean; - metadata?: ContractCallMetadata; -} + metadata: ContractCallMetadata; + assetAmount?: AssetFuelAmount; +}; export type SimplifiedFee = { total: BN; diff --git a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts index d4d61879c1..86ac855d28 100644 --- a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts +++ b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts @@ -100,12 +100,11 @@ function transformOperation( type, from: from?.address || '', to: to?.address || '', - amount: asset.amount ? new BN(asset.amount) : undefined, - assetId: asset.assetId, isFromCurrentAccount, + amount: new BN(asset.amount), + assetId: asset.assetId, metadata: { depth, - receiptType: receipt?.type, }, }; } @@ -117,7 +116,6 @@ function transformOperation( isFromCurrentAccount, metadata: { depth, - receiptType: receipt?.type, }, }; } From f096552b5054dc7215b3384772cc56591a5ceb94 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Sun, 26 Jan 2025 20:36:25 -0300 Subject: [PATCH 27/85] style: create custom AssetAmount --- .../operations/TxOperation.tsx | 134 +++++++++++++++++- 1 file changed, 127 insertions(+), 7 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx index 91b5b51892..f8aca35bb4 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx @@ -1,18 +1,24 @@ import { cssObj } from '@fuel-ui/css'; import { Avatar, + Badge, Box, ContentLoader, + Copyable, + Grid, Icon, IconButton, Text, + Tooltip, } from '@fuel-ui/react'; import type { AssetFuelAmount } from '@fuel-wallet/types'; -import { useEffect, useState } from 'react'; +import { bn } from 'fuels'; +import { useEffect, useRef, useState } from 'react'; import { useAccounts } from '~/systems/Account'; import { AssetsAmount } from '~/systems/Asset'; import { AssetsCache } from '~/systems/Asset/cache/AssetsCache'; import { shortAddress } from '~/systems/Core'; +import { formatAmount } from '~/systems/Core'; import { NetworkService } from '~/systems/Network/services/network'; import { TxCategory } from '../../../../types'; import type { SimplifiedOperation } from '../../../../types'; @@ -32,6 +38,96 @@ const AssetLoader = () => ( ); +type AssetsAmountProps = { + amounts: AssetFuelAmount[]; +}; + +const TxAssetsAmount = ({ amounts }: AssetsAmountProps) => { + const allEmptyAmounts = amounts.every((assetAmount) => + bn(assetAmount.amount).eq(0) + ); + + return ( + <> + {!allEmptyAmounts && ( + + + {amounts.map( + (assetAmount) => + bn(assetAmount.amount).gt(0) && ( + + ) + )} + + + )} + + ); +}; + +type AssetsAmountItemProps = { + assetAmount: AssetFuelAmount; +}; + +const TxAssetsAmountItem = ({ assetAmount }: AssetsAmountItemProps) => { + const { + name = '', + symbol, + icon, + assetId, + decimals, + amount, + isNft, + } = assetAmount || {}; + + const containerRef = useRef(null); + const [isTruncated, setIsTruncated] = useState(false); + + const formatted = formatAmount({ + amount, + options: { units: decimals || 0, precision: decimals || 0 }, + }); + + useEffect(() => { + if (containerRef.current) { + const amountElement = containerRef.current.querySelector('.amount-value'); + if (amountElement) { + setIsTruncated(amountElement.scrollWidth > amountElement.clientWidth); + } + } + }, []); + + return ( + + {icon ? ( + + ) : ( + + )} + + + + {formatted} + + + {symbol} + {isNft && ( + + NFT + + )} + + + ); +}; + export function TxOperation({ operation, showNesting = true, @@ -132,15 +228,16 @@ export function TxOperation({ return ( - + {/* From Address */} @@ -181,7 +278,7 @@ export function TxOperation({ - + {getOperationType()} @@ -196,7 +293,7 @@ export function TxOperation({ (isLoading ? ( ) : assetsAmount.length > 0 ? ( - + ) : null)} @@ -232,7 +329,7 @@ export function TxOperation({ /> - + ); } @@ -266,7 +363,6 @@ const styles = { color: '$gray8', }), spacer: cssObj({ - borderLeft: '1px solid #D9D9D9', minHeight: '14px', width: '2px', height: '100%', @@ -292,4 +388,28 @@ const styles = { fontWeight: '$medium', color: '#646464', }), + header: { + alignItems: 'center', + gap: '$2', + mb: '$3', + }, + title: { + fontSize: '$sm', + margin: 0, + }, + asset: { + alignItems: 'center', + gap: '$2', + }, + assetNft: { + padding: '$1 $2', + }, + amountContainer: { + fontWeight: '$semibold', + color: '#202020', + fontSize: '$sm', + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + }, }; From f15a9865605816c6c79c6bddfbf86d3ae21025be Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 27 Jan 2025 14:11:20 -0300 Subject: [PATCH 28/85] styles: simple fee --- .../components/TxViewSimple/TxFeeSimple.tsx | 101 +++++++++++++++++- 1 file changed, 97 insertions(+), 4 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx index 1e6277f689..b4926d8048 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx @@ -1,21 +1,82 @@ import { cssObj } from '@fuel-ui/css'; -import { Box, ContentLoader, Text } from '@fuel-ui/react'; +import { + Box, + ContentLoader, + RadioGroup, + RadioGroupItem, + Text, +} from '@fuel-ui/react'; +import type { BN } from 'fuels'; +import { DEFAULT_PRECISION, bn } from 'fuels'; +import { useEffect, useState } from 'react'; +import { formatAmount } from '~/systems/Core'; +import { TxService } from '../../services'; import type { SimplifiedFee } from '../../types'; -import { TxAssetAmount } from './TxOperationsSimple/operations/TxAssetAmount'; type TxFeeSimpleProps = { fee: SimplifiedFee; isLoading?: boolean; }; +type FeeOption = 'regular' | 'fast'; + export function TxFeeSimple({ fee, isLoading }: TxFeeSimpleProps) { + const [selectedOption, setSelectedOption] = useState('regular'); + const [tips, setTips] = useState<{ regularTip: BN; fastTip: BN }>(); + + useEffect(() => { + async function getTips() { + const { regularTip, fastTip } = await TxService.estimateDefaultTips(); + setTips({ regularTip, fastTip }); + } + getTips(); + }, []); + if (isLoading) return ; + const options = [ + { + id: 'regular', + name: 'Regular', + fee: tips ? fee.network.add(tips.regularTip) : fee.network, + }, + { + id: 'fast', + name: 'Fast', + fee: tips ? fee.network.add(tips.fastTip) : fee.network, + }, + ]; + return ( + Fee (network) - Network Fee - + + {options.map((option) => ( + setSelectedOption(option.id as FeeOption)} + > + setSelectedOption(option.id as FeeOption)} + /> + + + {option.fee + ? `${option.fee.format({ + minPrecision: DEFAULT_PRECISION, + precision: DEFAULT_PRECISION, + })} ETH` + : '--'} + + + ))} + ); @@ -44,5 +105,37 @@ const styles = { title: cssObj({ fontSize: '$sm', fontWeight: '$medium', + color: '#202020', + }), + option: cssObj({ + alignItems: 'center', + backgroundColor: 'white', + border: '1px solid #e0e0e0', + borderRadius: '10px', + color: '#646464', + cursor: 'pointer', + fontSize: '13px', + gap: '$3', + justifyContent: 'space-between', + padding: '$3', + transition: 'all 0.2s ease', + + '&:hover': { + backgroundColor: '#f0f0f0', + }, + }), + optionContent: cssObj({ + color: '#202020', + }), + optionLabel: cssObj({ + color: '#202020', + fontSize: '13px', + fontWeight: '$medium', + }), + radio: cssObj({ + cursor: 'pointer', + height: '16px', + margin: 0, + width: '16px', }), }; From 0214c0080b035e6bccd22c83df45857aabff434a Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 27 Jan 2025 15:05:10 -0300 Subject: [PATCH 29/85] feat: add custom fee options to simplified transaction view --- .../TxViewSimple/TxFeeOptionsSimple.tsx | 115 ++++++++++++++++++ .../components/TxViewSimple/TxFeeSimple.tsx | 45 ++++++- .../components/TxViewSimple/TxViewSimple.tsx | 24 +++- 3 files changed, 178 insertions(+), 6 deletions(-) create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx new file mode 100644 index 0000000000..ce83465361 --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx @@ -0,0 +1,115 @@ +import { cssObj } from '@fuel-ui/css'; +import { Box, Button, HStack, Input, Text, VStack } from '@fuel-ui/react'; +import type { BN } from 'fuels'; +import { DEFAULT_PRECISION } from 'fuels'; +import { useState } from 'react'; +import { createAmount } from '~/systems/Core/components/InputAmount/InputAmount'; +import { isAmountAllowed } from '~/systems/Core/components/InputAmount/InputAmount.utils'; + +type TxFeeOptionsSimpleProps = { + baseFee: BN; + onBack: () => void; + onTipChange?: (tip: BN) => void; +}; + +const DECIMAL_UNITS = 9; + +export function TxFeeOptionsSimple({ + baseFee, + onBack, + onTipChange, +}: TxFeeOptionsSimpleProps) { + const [customTip, setCustomTip] = useState('0'); + + const handleTipChange = (text: string) => { + setCustomTip(text); + const { amount } = createAmount(text, DECIMAL_UNITS); + onTipChange?.(amount); + }; + + const totalFee = createAmount(customTip, DECIMAL_UNITS).amount.add(baseFee); + + return ( + + Fee (network) + + + Fee + Tip + + + {' '} + ({totalFee.format({ minPrecision: DEFAULT_PRECISION })} ETH) + + + + + + + Gas limit + + + + + + Tip + + handleTipChange(e.target.value)} + /> + + + + + + ); +} + +const styles = { + content: cssObj({ + display: 'flex', + flexDirection: 'column', + gap: '$3', + padding: '$3', + }), + title: cssObj({ + fontSize: '$sm', + fontWeight: '$medium', + color: '#202020', + }), + card: cssObj({ + padding: '$4', + backgroundColor: 'white', + border: '1px solid #e0e0e0', + borderRadius: '10px', + fontSize: '13px', + }), + inputBox: cssObj({ + flex: 1, + display: 'flex', + flexDirection: 'column', + gap: '$2', + }), + backButton: cssObj({ + alignSelf: 'center', + color: '$accent11', + fontSize: '12px', + mt: '$2', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx index b4926d8048..0b832a998a 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx @@ -1,6 +1,7 @@ import { cssObj } from '@fuel-ui/css'; import { Box, + Button, ContentLoader, RadioGroup, RadioGroupItem, @@ -8,7 +9,7 @@ import { } from '@fuel-ui/react'; import type { BN } from 'fuels'; import { DEFAULT_PRECISION, bn } from 'fuels'; -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { formatAmount } from '~/systems/Core'; import { TxService } from '../../services'; import type { SimplifiedFee } from '../../types'; @@ -16,21 +17,41 @@ import type { SimplifiedFee } from '../../types'; type TxFeeSimpleProps = { fee: SimplifiedFee; isLoading?: boolean; + onCustomFees?: () => void; + onFeeSelect?: (tip: BN) => void; }; type FeeOption = 'regular' | 'fast'; -export function TxFeeSimple({ fee, isLoading }: TxFeeSimpleProps) { +export function TxFeeSimple({ + fee, + isLoading, + onCustomFees, + onFeeSelect, +}: TxFeeSimpleProps) { const [selectedOption, setSelectedOption] = useState('regular'); const [tips, setTips] = useState<{ regularTip: BN; fastTip: BN }>(); + const previousDefaultTip = useRef(); useEffect(() => { async function getTips() { const { regularTip, fastTip } = await TxService.estimateDefaultTips(); setTips({ regularTip, fastTip }); + // Set initial tip + previousDefaultTip.current = regularTip; + onFeeSelect?.(regularTip); } getTips(); - }, []); + }, [onFeeSelect]); + + const handleOptionSelect = (option: FeeOption) => { + setSelectedOption(option); + if (!tips) return; + + const newTip = option === 'regular' ? tips.regularTip : tips.fastTip; + previousDefaultTip.current = newTip; + onFeeSelect?.(newTip); + }; if (isLoading) return ; @@ -56,14 +77,14 @@ export function TxFeeSimple({ fee, isLoading }: TxFeeSimpleProps) { setSelectedOption(option.id as FeeOption)} + onClick={() => handleOptionSelect(option.id as FeeOption)} > setSelectedOption(option.id as FeeOption)} + onChange={() => handleOptionSelect(option.id as FeeOption)} /> @@ -77,6 +98,14 @@ export function TxFeeSimple({ fee, isLoading }: TxFeeSimpleProps) { ))} + ); @@ -138,4 +167,10 @@ const styles = { margin: 0, width: '16px', }), + customFeesBtn: cssObj({ + alignSelf: 'center', + color: '$accent11', + fontSize: '$sm', + mt: '$2', + }), }; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx index e9d712b25b..875163119d 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx @@ -1,6 +1,10 @@ import { cssObj } from '@fuel-ui/css'; import { Box } from '@fuel-ui/react'; +import type { BN } from 'fuels'; +import { useState } from 'react'; import type { SimplifiedTransactionViewProps } from '../../types'; +import { TxFeeOptions } from '../TxFeeOptions/TxFeeOptions'; +import { TxFeeOptionsSimple } from './TxFeeOptionsSimple'; import { TxFeeSimple } from './TxFeeSimple'; import { TxHeaderSimple } from './TxHeaderSimple'; import { TxOperationsList } from './TxOperationsSimple/TxOperationsList'; @@ -11,13 +15,31 @@ export function TxViewSimple({ isLoading, footer, }: SimplifiedTransactionViewProps) { + const [isCustomFees, setIsCustomFees] = useState(false); + const [_selectedTip, setSelectedTip] = useState(); + return ( {showDetails && ( - + + {isCustomFees ? ( + setIsCustomFees(false)} + onTipChange={setSelectedTip} + /> + ) : ( + setIsCustomFees(true)} + onFeeSelect={setSelectedTip} + /> + )} + )} {footer} From 37281516092e9cf6638d49075347e1cac39dd2e9 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 27 Jan 2025 15:55:35 -0300 Subject: [PATCH 30/85] style: update TxOperationsGroup with new design and interaction --- .../TxOperationsSimple/TxOperationsGroup.tsx | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx index f37c54422d..1d2635a2a5 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx @@ -28,14 +28,15 @@ export function TxOperationsGroup({ return ( - + {operations.length} - {title} ({operations.length}) + {title} + {isExpanded && ( Date: Mon, 27 Jan 2025 16:42:08 -0300 Subject: [PATCH 31/85] style: ui --- .../Transaction/components/TxViewSimple/TxHeaderSimple.tsx | 3 ++- .../Transaction/components/TxViewSimple/TxViewSimple.tsx | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx index 3f689623ec..f140629a1c 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx @@ -17,7 +17,8 @@ export function TxHeaderSimple({ isLoading }: TxHeaderSimpleProps) { return ( - Review Transaction + {/* Disabled while the new Wallet header is not implemented */} + {/* Review Transaction */} diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx index 875163119d..041d399cc1 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx @@ -55,8 +55,7 @@ const styles = { }), content: cssObj({ flex: 1, - overflowY: 'auto', - padding: '$4', + padding: '$1', }), }; From e5431600ffbc121ad5e6b1c37c818ea3674dc21f Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 27 Jan 2025 16:55:45 -0300 Subject: [PATCH 32/85] style: adjust tx header --- .../TxViewSimple/TxHeaderSimple.tsx | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx index f140629a1c..00d7135823 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx @@ -21,7 +21,7 @@ export function TxHeaderSimple({ isLoading }: TxHeaderSimpleProps) { {/* Review Transaction */} - + Double-check the details of your transaction before submitting. @@ -47,7 +47,7 @@ const styles = { marginBottom: '$4', backgroundColor: '$white', borderBottom: '1px solid $border', - padding: '$4', + padding: '12px 18px', '.fuel_Text': { '&[as="h2"]': { @@ -61,21 +61,10 @@ const styles = { display: 'flex', alignItems: 'center', gap: '$2', - fontSize: '$sm', + fontSize: '12px', + color: '#646464', fontWeight: '$normal', marginBottom: '$2', - - '.fuel_Text': { - '&[as="h1"]': { - fontSize: '$sm', - margin: 0, - fontWeight: '$normal', - }, - }, - - '.fuel_Icon': { - color: '$intentsWarning10', - }, }), root: cssObj({ padding: '$3', From 36aa2c678b44bce8eeeaedb8f3eb15d5615b34ac Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 27 Jan 2025 18:00:16 -0300 Subject: [PATCH 33/85] style: animate groups --- .../TxOperationsSimple/TxOperationsGroup.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx index 1d2635a2a5..e87e69873b 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx @@ -1,6 +1,8 @@ import { cssObj } from '@fuel-ui/css'; import { Box, Icon, Text } from '@fuel-ui/react'; import { useState } from 'react'; +import { animations } from '~/systems/Core'; +import { MotionBox } from '~/systems/Core/components/Motion'; import type { SimplifiedOperation } from '../../../types'; import { TxOperation } from './operations/TxOperation'; @@ -39,8 +41,8 @@ export function TxOperationsGroup({ /> {isExpanded && ( - e.stopPropagation()} > @@ -51,7 +53,7 @@ export function TxOperationsGroup({ showNesting={showNesting} /> ))} - + )} ); @@ -81,7 +83,11 @@ const styles = { color: '#202020', fontWeight: '$medium', }), - content: cssObj({}), + content: cssObj({ + display: 'flex', + flexDirection: 'column', + gap: '$1', + }), count: cssObj({ backgroundColor: 'white', borderRadius: '$full', From cf68b40eb3983ab31e4563a6b093c7d83dcc412e Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Tue, 28 Jan 2025 13:52:00 -0300 Subject: [PATCH 34/85] styles: fee title --- .../TxViewSimple/TxFeeOptionsSimple.tsx | 1 - .../components/TxViewSimple/TxFeeSimple.tsx | 8 +---- .../TxViewSimple/TxHeaderSimple.tsx | 20 +++-------- .../components/TxViewSimple/TxViewSimple.tsx | 34 ++++++++++++++++--- 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx index ce83465361..d22b8ef086 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx @@ -31,7 +31,6 @@ export function TxFeeOptionsSimple({ return ( - Fee (network) Fee + Tip diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx index 0b832a998a..61e1fe6e69 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx @@ -70,8 +70,7 @@ export function TxFeeSimple({ return ( - Fee (network) - + {options.map((option) => ( Review Transaction */} - - - Double-check the details of your transaction before submitting. - + + Double-check the details of your transaction before submitting. ); @@ -44,18 +42,10 @@ const styles = { display: 'flex', flexDirection: 'column', gap: '$2', - marginBottom: '$4', + marginBottom: '$1', backgroundColor: '$white', borderBottom: '1px solid $border', padding: '12px 18px', - - '.fuel_Text': { - '&[as="h2"]': { - fontSize: '$xl', - fontWeight: '$normal', - margin: 0, - }, - }, }), warning: cssObj({ display: 'flex', @@ -63,7 +53,7 @@ const styles = { gap: '$2', fontSize: '12px', color: '#646464', - fontWeight: '$normal', + fontWeight: '500', marginBottom: '$2', }), root: cssObj({ @@ -87,7 +77,7 @@ const styles = { icon: cssObj({ width: '20px', height: '20px', - color: '$gray9', + color: '#8d8d8d', }), status: cssObj({ display: 'flex', diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx index 041d399cc1..024da9b204 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx @@ -1,5 +1,5 @@ import { cssObj } from '@fuel-ui/css'; -import { Box } from '@fuel-ui/react'; +import { Box, Icon, Text } from '@fuel-ui/react'; import type { BN } from 'fuels'; import { useState } from 'react'; import type { SimplifiedTransactionViewProps } from '../../types'; @@ -19,12 +19,16 @@ export function TxViewSimple({ const [_selectedTip, setSelectedTip] = useState(); return ( - + {showDetails && ( + + + Fee (network) + {isCustomFees ? ( - + ); } @@ -52,10 +56,32 @@ const styles = { height: '100%', display: 'flex', flexDirection: 'column', + background: '#F0F0F0', + overflow: 'auto', }), content: cssObj({ flex: 1, - padding: '$1', + padding: '$1 $2', + }), + title: cssObj({ + fontSize: '$sm', + fontWeight: '$medium', + color: '#202020', + }), + feeContainer: cssObj({ + padding: '32px 0 0 20px', + }), + icon: cssObj({ + border: '1.5px solid rgb(100, 100, 100)', + borderRadius: '50%', + width: '20px', + height: '20px', + boxSizing: 'border-box', + '& svg': { + width: '16px', + height: '16px', + strokeWidth: '2.5px', + }, }), }; From cf05d80bab867b11610a441272134151359bf5cc Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Tue, 28 Jan 2025 13:52:13 -0300 Subject: [PATCH 35/85] style: avatar --- .../operations/TxOperation.tsx | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx index f8aca35bb4..18f25160a4 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx @@ -237,11 +237,15 @@ export function TxOperation({ width: '100%', marginBottom: '$2', columnGap: '$2', - rowGap: '$1', + rowGap: '1px', })} > {/* From Address */} - + {/* To Address */} - + Date: Wed, 29 Jan 2025 13:21:53 -0300 Subject: [PATCH 36/85] style: refine TxOperationsGroup layout and styling --- .../TxOperationsSimple/TxOperationsGroup.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx index e87e69873b..22723daff0 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx @@ -28,7 +28,7 @@ export function TxOperationsGroup({ }; return ( - + {operations.length} @@ -64,17 +64,20 @@ const styles = { marginTop: '$2', backgroundColor: '#E0E0E0', borderRadius: '12px', + minHeight: '56px', + alignItems: 'center', + justifyContent: 'center', }), header: cssObj({ display: 'flex', - alignItems: 'center', gap: '$2', - padding: '$2', cursor: 'pointer', border: 'none', width: '100%', transition: 'all 0.2s ease', backgroundColor: 'transparent', + padding: '0 20px 0 30px', + alignItems: 'center', }), icon: cssObj({ color: '#202020', @@ -89,9 +92,8 @@ const styles = { gap: '$1', }), count: cssObj({ - backgroundColor: 'white', + backgroundColor: '$white', borderRadius: '$full', - padding: '$1', color: '#202020', border: '1.5px solid #8D8D8D', width: '20px', @@ -99,6 +101,10 @@ const styles = { display: 'flex', alignItems: 'center', justifyContent: 'center', + fontSize: '13px', + fontWeight: '600', + marginTop: '18px', + marginBottom: '18px', }), chevron: cssObj({ transform: 'rotate(0deg)', From 89ead3f39c7e92a33fa71856971fd0a6971db1ae Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Wed, 29 Jan 2025 13:58:25 -0300 Subject: [PATCH 37/85] feat: improve TxOperationsGroup animation and layout --- .../TxOperationsSimple/TxOperationsGroup.tsx | 47 ++++++++++++------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx index 22723daff0..b5362660b8 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx @@ -28,33 +28,44 @@ export function TxOperationsGroup({ }; return ( - - - {operations.length} - - {title} - + + + + {operations.length} + + {title} + + - {isExpanded && ( - e.stopPropagation()} - > - {operations.map((operation, index) => ( + e.stopPropagation()} + > + {operations.map((operation, index) => + isExpanded ? ( - ))} - - )} + ) : null + )} + ); } @@ -67,6 +78,7 @@ const styles = { minHeight: '56px', alignItems: 'center', justifyContent: 'center', + transition: 'all 0.2s ease', }), header: cssObj({ display: 'flex', @@ -76,7 +88,7 @@ const styles = { width: '100%', transition: 'all 0.2s ease', backgroundColor: 'transparent', - padding: '0 20px 0 30px', + padding: '0 22px 0 22px', alignItems: 'center', }), icon: cssObj({ @@ -85,6 +97,7 @@ const styles = { title: cssObj({ color: '#202020', fontWeight: '$medium', + textAlign: 'left', }), content: cssObj({ display: 'flex', From 935a38c723ae6d5d3c10e04324e7895145ab5f0d Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Wed, 29 Jan 2025 15:36:34 -0300 Subject: [PATCH 38/85] refactor: simplify TxOperation component and asset rendering --- .../operations/TxOperation.tsx | 458 ++++++++---------- 1 file changed, 193 insertions(+), 265 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx index 18f25160a4..945b3349e0 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx @@ -15,7 +15,6 @@ import type { AssetFuelAmount } from '@fuel-wallet/types'; import { bn } from 'fuels'; import { useEffect, useRef, useState } from 'react'; import { useAccounts } from '~/systems/Account'; -import { AssetsAmount } from '~/systems/Asset'; import { AssetsCache } from '~/systems/Asset/cache/AssetsCache'; import { shortAddress } from '~/systems/Core'; import { formatAmount } from '~/systems/Core'; @@ -28,104 +27,115 @@ type TxOperationProps = { showNesting?: boolean; }; -const SpacerComponent = () => { - return ; -}; - -const AssetLoader = () => ( - - - -); - -type AssetsAmountProps = { - amounts: AssetFuelAmount[]; -}; - -const TxAssetsAmount = ({ amounts }: AssetsAmountProps) => { +const renderAssets = (amounts: AssetFuelAmount[]) => { const allEmptyAmounts = amounts.every((assetAmount) => bn(assetAmount.amount).eq(0) ); + if (allEmptyAmounts) return null; + return ( - <> - {!allEmptyAmounts && ( - - - {amounts.map( - (assetAmount) => - bn(assetAmount.amount).gt(0) && ( - - ) - )} - - - )} - + + + {amounts.map( + (assetAmount) => + bn(assetAmount.amount).gt(0) && ( + + + + + + {formatAmount({ + amount: assetAmount.amount, + options: { + units: assetAmount.decimals || 0, + precision: assetAmount.decimals || 0, + }, + })} + + + {assetAmount.symbol} + {assetAmount.isNft && ( + + NFT + + )} + + + ) + )} + + ); }; -type AssetsAmountItemProps = { - assetAmount: AssetFuelAmount; -}; +const fetchAssetsAmount = async (operation: SimplifiedOperation) => { + try { + const coins = []; + if (operation.amount && operation.assetId) { + coins.push({ + amount: operation.amount, + assetId: operation.assetId, + }); + } else if (operation.metadata?.amount && operation.metadata?.assetId) { + coins.push({ + amount: operation.metadata.amount, + assetId: operation.metadata.assetId, + }); + } -const TxAssetsAmountItem = ({ assetAmount }: AssetsAmountItemProps) => { - const { - name = '', - symbol, - icon, - assetId, - decimals, - amount, - isNft, - } = assetAmount || {}; + if (!coins.length) return []; - const containerRef = useRef(null); - const [isTruncated, setIsTruncated] = useState(false); + const assetsCache = AssetsCache.getInstance(); + const network = await NetworkService.getSelectedNetwork(); + if (!network) return []; - const formatted = formatAmount({ - amount, - options: { units: decimals || 0, precision: decimals || 0 }, - }); + const assetsWithAmount = await Promise.all( + coins.map(async (operationCoin) => { + const assetCached = await assetsCache.getAsset({ + chainId: network.chainId, + assetId: operationCoin.assetId, + dbAssets: [], + save: false, + }); - useEffect(() => { - if (containerRef.current) { - const amountElement = containerRef.current.querySelector('.amount-value'); - if (amountElement) { - setIsTruncated(amountElement.scrollWidth > amountElement.clientWidth); - } - } - }, []); + if (!assetCached) return null; - return ( - - {icon ? ( - - ) : ( - - )} - - - - {formatted} - - - {symbol} - {isNft && ( - - NFT - - )} - - - ); + return { + type: 'fuel', + chainId: network.chainId, + name: assetCached.name, + symbol: assetCached.symbol, + decimals: assetCached.decimals, + icon: assetCached.icon, + assetId: operationCoin.assetId, + amount: operationCoin.amount, + } as AssetFuelAmount; + }) + ); + + const filteredAssets = assetsWithAmount.filter( + (a): a is AssetFuelAmount => a !== null + ); + return filteredAssets; + } catch (error) { + console.error('Error fetching assets:', error); + return []; + } }; export function TxOperation({ @@ -138,7 +148,6 @@ export function TxOperation({ const depth = metadata?.depth || 0; const { accounts } = useAccounts(); const [assetsAmount, setAssetsAmount] = useState([]); - const [isLoading, setIsLoading] = useState(false); const accountFrom = accounts?.find( (acc) => acc.address.toLowerCase() === operation.from.toLowerCase() @@ -148,69 +157,8 @@ export function TxOperation({ ); useEffect(() => { - const fetchAssetsAmount = async () => { - try { - const coins = []; - if (operation.amount && operation.assetId) { - coins.push({ - amount: operation.amount, - assetId: operation.assetId, - }); - } else if (metadata?.amount && metadata?.assetId) { - coins.push({ - amount: metadata.amount, - assetId: metadata.assetId, - }); - } - - if (!coins.length) return; - - setIsLoading(true); - const assetsCache = AssetsCache.getInstance(); - const network = await NetworkService.getSelectedNetwork(); - if (!network) return; - - console.log('Fetching assets for:', coins); - - const assetsWithAmount = await Promise.all( - coins.map(async (operationCoin) => { - const assetCached = await assetsCache.getAsset({ - chainId: network.chainId, - assetId: operationCoin.assetId, - dbAssets: [], - save: false, - }); - - if (!assetCached) return null; - - return { - type: 'fuel', - chainId: network.chainId, - name: assetCached.name, - symbol: assetCached.symbol, - decimals: assetCached.decimals, - icon: assetCached.icon, - assetId: operationCoin.assetId, - amount: operationCoin.amount, - } as AssetFuelAmount; - }) - ); - - const filteredAssets = assetsWithAmount.filter( - (a): a is AssetFuelAmount => a !== null - ); - console.log('Setting assets:', filteredAssets); - setAssetsAmount(filteredAssets); - } catch (error) { - console.error('Error fetching assets:', error); - setAssetsAmount([]); - } finally { - setIsLoading(false); - } - }; - - fetchAssetsAmount(); - }, []); // Only run once when component mounts + fetchAssetsAmount(operation).then(setAssetsAmount); + }, [operation]); const getOperationType = () => { if (isContract) return 'Calls contract (sending funds)'; @@ -227,129 +175,109 @@ export function TxOperation({ (metadata?.amount && metadata?.assetId); return ( - - - - {/* From Address */} - - - - - - {accountFrom?.name || 'Unknown'} - - {isContract && ( - - - Contract - - - )} - - {shortAddress(operation.from)} - - navigator.clipboard.writeText(operation.from)} - /> - + + + {/* From Address */} + + + + + + {accountFrom?.name || 'Unknown'} + + {isContract && ( + + + Contract + + + )} + + {shortAddress(operation.from)} + + navigator.clipboard.writeText(operation.from)} + /> + - {/* Spacer and Arrow */} - - - - - - - - - {getOperationType()} - + {/* Spacer and Arrow */} + + + + + + + + + {getOperationType()} + - {/* Asset Amount */} - - - - - {shouldShowAssetAmount && - (isLoading ? ( - - ) : assetsAmount.length > 0 ? ( - - ) : null)} - + {/* Asset Amount */} + + + + + {shouldShowAssetAmount && + assetsAmount.length > 0 && + renderAssets(assetsAmount)} + - {/* To Address */} - - - - - - {accountTo?.name || 'Unknown'} - - {isContract && ( - - - Contract - - - )} - - {shortAddress(operation.to)} - - navigator.clipboard.writeText(operation.to)} - /> - + {/* To Address */} + + - - + + + {accountTo?.name || 'Unknown'} + + {isContract && ( + + + Contract + + + )} + + {shortAddress(operation.to)} + + navigator.clipboard.writeText(operation.to)} + /> + + + ); } // we also have the operations not related to the account in a group, and intermediate contract calls const styles = { - root: cssObj({ - padding: '$1', - backgroundColor: '#E0E0E0', - borderRadius: '12px', - width: '100%', - boxSizing: 'border-box', - }), contentCol: cssObj({ display: 'flex', backgroundColor: 'white', From e04219015ad253e01490c0ba83a87852e910c1c5 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Wed, 29 Jan 2025 16:09:37 -0300 Subject: [PATCH 39/85] style: enhance transaction operations list and component styling --- .../TxOperationsSimple/TxOperationsGroup.tsx | 8 ++++---- .../TxOperationsSimple/TxOperationsList.tsx | 15 +++++++++++---- .../TxOperationsSimple/operations/TxOperation.tsx | 8 +++----- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx index b5362660b8..d12f4939da 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx @@ -28,7 +28,7 @@ export function TxOperationsGroup({ }; return ( - + - + ); } @@ -79,6 +78,7 @@ const styles = { alignItems: 'center', justifyContent: 'center', transition: 'all 0.2s ease', + paddingBottom: '4px', }), header: cssObj({ display: 'flex', @@ -102,7 +102,7 @@ const styles = { content: cssObj({ display: 'flex', flexDirection: 'column', - gap: '$1', + gap: '4px 0', }), count: cssObj({ backgroundColor: '$white', diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx index 440c82ce82..77bd82137b 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx @@ -59,11 +59,18 @@ export function TxOperationsList({ {/* Main operations (transfers and root contract calls related to current account) */} {mainOperations.map((operation, index) => ( - + css={{ + backgroundColor: '#E0E0E0', + borderRadius: '12px', + width: '100%', + boxSizing: 'border-box', + padding: '4px 0', + }} + > + + ))} {/* Other root operations not related to current account */} diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx index 945b3349e0..3b31e9d4c3 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx @@ -3,9 +3,6 @@ import { Avatar, Badge, Box, - ContentLoader, - Copyable, - Grid, Icon, IconButton, Text, @@ -13,7 +10,7 @@ import { } from '@fuel-ui/react'; import type { AssetFuelAmount } from '@fuel-wallet/types'; import { bn } from 'fuels'; -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useAccounts } from '~/systems/Account'; import { AssetsCache } from '~/systems/Asset/cache/AssetsCache'; import { shortAddress } from '~/systems/Core'; @@ -276,7 +273,7 @@ export function TxOperation({ ); } -// we also have the operations not related to the account in a group, and intermediate contract calls + const styles = { contentCol: cssObj({ display: 'flex', @@ -286,6 +283,7 @@ const styles = { borderRadius: '8px', minWidth: 0, padding: '14px 12px', + margin: '0 4px', }), blue: cssObj({ fontSize: '$sm', From 5d620d49c7a1929fbcda3b006319cd5c3162fffb Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Wed, 29 Jan 2025 16:48:47 -0300 Subject: [PATCH 40/85] style: refine TxOperationsGroup styling and layout details --- .../TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx index d12f4939da..468f5ad465 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx @@ -77,8 +77,8 @@ const styles = { minHeight: '56px', alignItems: 'center', justifyContent: 'center', - transition: 'all 0.2s ease', paddingBottom: '4px', + boxSizing: 'border-box', }), header: cssObj({ display: 'flex', @@ -86,9 +86,8 @@ const styles = { cursor: 'pointer', border: 'none', width: '100%', - transition: 'all 0.2s ease', backgroundColor: 'transparent', - padding: '0 22px 0 22px', + padding: '17px 22px 12px 22px', alignItems: 'center', }), icon: cssObj({ @@ -116,8 +115,6 @@ const styles = { justifyContent: 'center', fontSize: '13px', fontWeight: '600', - marginTop: '18px', - marginBottom: '18px', }), chevron: cssObj({ transform: 'rotate(0deg)', From 998bf746358b405556f5f1d7d14ac4538aa46210 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Wed, 29 Jan 2025 17:05:03 -0300 Subject: [PATCH 41/85] feat: pass current account to transaction operations list --- .../Transaction/components/TxViewSimple/TxViewSimple.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx index 024da9b204..54f00492b9 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx @@ -2,8 +2,8 @@ import { cssObj } from '@fuel-ui/css'; import { Box, Icon, Text } from '@fuel-ui/react'; import type { BN } from 'fuels'; import { useState } from 'react'; +import { useAccounts } from '~/systems/Account'; import type { SimplifiedTransactionViewProps } from '../../types'; -import { TxFeeOptions } from '../TxFeeOptions/TxFeeOptions'; import { TxFeeOptionsSimple } from './TxFeeOptionsSimple'; import { TxFeeSimple } from './TxFeeSimple'; import { TxHeaderSimple } from './TxHeaderSimple'; @@ -17,12 +17,16 @@ export function TxViewSimple({ }: SimplifiedTransactionViewProps) { const [isCustomFees, setIsCustomFees] = useState(false); const [_selectedTip, setSelectedTip] = useState(); + const { account } = useAccounts(); return ( - + {showDetails && ( From ed39925332b971caba1385ef0fb9bcbe80f1860e Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Wed, 29 Jan 2025 17:05:24 -0300 Subject: [PATCH 42/85] chore: update fuels dependency to local development version --- .../TxOperationsSimple/TxOperationsGroup.tsx | 6 ++++-- .../TxOperationsSimple/TxOperationsList.tsx | 10 ++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx index 468f5ad465..ee99e29c3f 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx @@ -10,12 +10,14 @@ type TxOperationsGroupProps = { title: string; operations: SimplifiedOperation[]; showNesting?: boolean; + numberLabel?: string; }; export function TxOperationsGroup({ title, operations, showNesting, + numberLabel, }: TxOperationsGroupProps) { const [isExpanded, setIsExpanded] = useState(false); @@ -36,7 +38,7 @@ export function TxOperationsGroup({ justify="space-between" > - {operations.length} + {numberLabel} {title} @@ -103,7 +105,7 @@ const styles = { flexDirection: 'column', gap: '4px 0', }), - count: cssObj({ + numberLabel: cssObj({ backgroundColor: '$white', borderRadius: '$full', color: '#202020', diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx index 77bd82137b..384623134a 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx @@ -24,8 +24,11 @@ export function TxOperationsList({ const depth = op.metadata?.depth || 0; const isTransfer = op.type === TxCategory.SEND; const isFromCurrentAccount = - currentAccount && op.from === currentAccount; - const isToCurrentAccount = currentAccount && op.to === currentAccount; + currentAccount && + op.from.toLowerCase() === currentAccount.toLowerCase(); + const isToCurrentAccount = + currentAccount && + op.to.toLowerCase() === currentAccount.toLowerCase(); // All transfers go to main list if (isTransfer) { @@ -67,6 +70,7 @@ export function TxOperationsList({ width: '100%', boxSizing: 'border-box', padding: '4px 0', + marginBottom: '16px', }} > @@ -78,6 +82,7 @@ export function TxOperationsList({ title="Other Contract Calls" operations={otherRootOperations} showNesting={false} + numberLabel="1" /> {/* Intermediate operations with nesting */} @@ -85,6 +90,7 @@ export function TxOperationsList({ title="Intermediate Operations" operations={intermediateOperations} showNesting={true} + numberLabel={otherRootOperations.length ? '2' : '1'} /> ); From 32d598991d2883b99e6a4987e4304822017c4968 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Wed, 29 Jan 2025 18:15:16 -0300 Subject: [PATCH 43/85] fix: types --- packages/app/src/systems/Transaction/types.ts | 142 ------------------ .../app/src/systems/Transaction/types.tsx | 14 +- 2 files changed, 9 insertions(+), 147 deletions(-) delete mode 100644 packages/app/src/systems/Transaction/types.ts diff --git a/packages/app/src/systems/Transaction/types.ts b/packages/app/src/systems/Transaction/types.ts deleted file mode 100644 index e6f42ca95c..0000000000 --- a/packages/app/src/systems/Transaction/types.ts +++ /dev/null @@ -1,142 +0,0 @@ -import type { AssetFuelAmount } from '@fuel-wallet/types'; -import type { - AddressType, - BN, - CallResult, - CoinTransactionRequestInput, - CoinTransactionRequestOutput, - InputContract, - OutputContract, - OutputContractCreated, - Receipt, - ReceiptType, - TransactionRequest, - TransactionRequestInput, - TransactionRequestLike, - TransactionStatus, - TransactionSummary, -} from 'fuels'; -import type { OperationFunctionCall } from 'fuels'; -import type { ReactNode } from 'react'; - -export enum TxCategory { - SEND = 'send', - RECEIVE = 'receive', - CONTRACTCALL = 'contractCall', - SCRIPT = 'script', - PREDICATE = 'predicate', -} - -export type TxRecipientAddress = { - address: string; - type: AddressType; -}; - -export type TxRequest = TransactionRequestLike; -export type TxSimulateResult = CallResult; -export type TxInput = TransactionRequestInput; -export type TxInputCoin = CoinTransactionRequestInput; -export type TxInputContract = InputContract; -export type TxOutputCoin = CoinTransactionRequestOutput; -export type TxOutputContract = OutputContract; -export type TxOutputContractCreated = OutputContractCreated; - -export type TransactionCursor = { - address: string; - size: number; - providerUrl: string; - endCursor: string; -}; - -export enum OperationDirection { - to = 'To', - from = 'From', - unknown = 'Unknown', -} - -export type ContractCallMetadata = { - contractId?: string; - functionName?: string; - functionData?: OperationFunctionCall; - amount?: BN; - assetId?: string; - depth?: number; - receiptType?: ReceiptType; - assetAmount?: AssetFuelAmount; -}; - -export type SwapMetadata = { - isSwap: boolean; - receiveAmount: string; - receiveAssetId: string; - totalAmount?: BN; - operationCount?: number; - receipts?: Receipt[]; - depth?: number; - parentReceiptId?: string; -}; - -export type SimplifiedOperation = { - type: TxCategory; - from: string; - to: string; - amount?: BN; - assetId?: string; - isFromCurrentAccount: boolean; - metadata: ContractCallMetadata; - assetAmount?: AssetFuelAmount; -}; - -export type SimplifiedFee = { - total: BN; - network: BN; - tip?: BN; - gasUsed?: BN; - gasPrice?: BN; -}; - -export type SimplifiedTransaction = { - id?: string; - operations: SimplifiedOperation[]; - timestamp?: Date; - fee: SimplifiedFee; - origin?: { - name: string; - favicon?: string; - url?: string; - }; - original: { - summary: TransactionSummary; - request?: TransactionRequest; - }; -}; - -export type SimplifiedTransactionViewProps = { - transaction: SimplifiedTransaction; - showDetails?: boolean; - isLoading?: boolean; - footer?: ReactNode; -}; - -export interface AssetFlow { - assetId: string; - amount: BN; - from: string; - to: string; - type: 'in' | 'out'; // from perspective of current user -} - -export interface SimplifiedAssetFlows { - assetsIn: AssetFlow[]; - assetsOut: AssetFlow[]; - fees: { - gasUsed: BN; - networkFee: BN; - tip: BN; - otherFees: AssetFlow[]; // Other fees paid in various assets - }; - contractInteractions: Array<{ - contractId: string; - functionName?: string; - }>; -} diff --git a/packages/app/src/systems/Transaction/types.tsx b/packages/app/src/systems/Transaction/types.tsx index c4b3e7b690..3f4e956309 100644 --- a/packages/app/src/systems/Transaction/types.tsx +++ b/packages/app/src/systems/Transaction/types.tsx @@ -1,3 +1,4 @@ +import type { AssetFuelAmount } from '@fuel-wallet/types'; import type { AddressType, BN, @@ -8,10 +9,10 @@ import type { OutputContract, OutputContractCreated, Receipt, + ReceiptType, TransactionRequest, TransactionRequestInput, TransactionRequestLike, - TransactionStatus, TransactionSummary, } from 'fuels'; import type { OperationFunctionCall } from 'fuels'; @@ -58,7 +59,9 @@ export type ContractCallMetadata = { functionData?: OperationFunctionCall; amount?: BN; assetId?: string; - operationCount?: number; + depth?: number; + receiptType?: ReceiptType; + assetAmount?: AssetFuelAmount; }; export type SwapMetadata = { @@ -72,15 +75,16 @@ export type SwapMetadata = { parentReceiptId?: string; }; -export interface SimplifiedOperation { +export type SimplifiedOperation = { type: TxCategory; from: string; to: string; amount?: BN; assetId?: string; isFromCurrentAccount: boolean; - metadata?: ContractCallMetadata; -} + metadata: ContractCallMetadata; + assetAmount?: AssetFuelAmount; +}; export type SimplifiedFee = { total: BN; From 31d2981f319102082ff25b3df3cb5500f77c8443 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Thu, 30 Jan 2025 14:56:14 -0300 Subject: [PATCH 44/85] style: update TxFeeOptionsSimple color scheme --- .../components/TxViewSimple/TxFeeOptionsSimple.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx index d22b8ef086..f34166bb63 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx @@ -90,12 +90,12 @@ const styles = { title: cssObj({ fontSize: '$sm', fontWeight: '$medium', - color: '#202020', + color: '$gray12', }), card: cssObj({ padding: '$4', - backgroundColor: 'white', - border: '1px solid #e0e0e0', + backgroundColor: '$gray1', + border: '1px solid $gray6', borderRadius: '10px', fontSize: '13px', }), From 45b62947ec8539f67bcb5a9cddecc2f05561be05 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Thu, 30 Jan 2025 14:56:42 -0300 Subject: [PATCH 45/85] style: enhance TxFeeSimple component with refined styling and formatting --- .../Transaction/components/TxViewSimple/TxFeeSimple.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx index 61e1fe6e69..cc966b4f15 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx @@ -8,9 +8,8 @@ import { Text, } from '@fuel-ui/react'; import type { BN } from 'fuels'; -import { DEFAULT_PRECISION, bn } from 'fuels'; +import { DEFAULT_PRECISION } from 'fuels'; import { useEffect, useRef, useState } from 'react'; -import { formatAmount } from '~/systems/Core'; import { TxService } from '../../services'; import type { SimplifiedFee } from '../../types'; @@ -132,7 +131,7 @@ const styles = { }), option: cssObj({ alignItems: 'center', - backgroundColor: 'white', + backgroundColor: '$white', border: '1px solid #e0e0e0', borderRadius: '10px', color: '#646464', @@ -142,6 +141,7 @@ const styles = { justifyContent: 'space-between', padding: '$3', transition: 'all 0.2s ease', + boxShadow: '0px 2px 6px -1px #2020201A, 0px 0px 0px 1px #2020201F', '&:hover': { backgroundColor: '#f0f0f0', From 59ede22350e71ef7f65846e0d47a20a0f4cdfa98 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Thu, 30 Jan 2025 14:56:55 -0300 Subject: [PATCH 46/85] style: update TxViewSimple component color tokens --- .../Transaction/components/TxViewSimple/TxViewSimple.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx index 54f00492b9..c48231d2f2 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx @@ -60,7 +60,7 @@ const styles = { height: '100%', display: 'flex', flexDirection: 'column', - background: '#F0F0F0', + background: '$gray3', overflow: 'auto', }), content: cssObj({ @@ -70,13 +70,13 @@ const styles = { title: cssObj({ fontSize: '$sm', fontWeight: '$medium', - color: '#202020', + color: '$gray12', }), feeContainer: cssObj({ padding: '32px 0 0 20px', }), icon: cssObj({ - border: '1.5px solid rgb(100, 100, 100)', + border: '1.5px solid $gray9', borderRadius: '50%', width: '20px', height: '20px', From d82ef93373c5b5f94d51a9ca288930e6b5cd7a3a Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Thu, 30 Jan 2025 14:57:26 -0300 Subject: [PATCH 47/85] style: update TxOperationsGroup color tokens and styling --- .../TxOperationsSimple/TxOperationsGroup.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx index ee99e29c3f..ea7a2dd541 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx @@ -1,7 +1,6 @@ import { cssObj } from '@fuel-ui/css'; import { Box, Icon, Text } from '@fuel-ui/react'; import { useState } from 'react'; -import { animations } from '~/systems/Core'; import { MotionBox } from '~/systems/Core/components/Motion'; import type { SimplifiedOperation } from '../../../types'; import { TxOperation } from './operations/TxOperation'; @@ -74,7 +73,7 @@ export function TxOperationsGroup({ const styles = { root: cssObj({ marginTop: '$2', - backgroundColor: '#E0E0E0', + backgroundColor: '$gray5', borderRadius: '12px', minHeight: '56px', alignItems: 'center', @@ -96,7 +95,7 @@ const styles = { color: '#202020', }), title: cssObj({ - color: '#202020', + color: '$gray12', fontWeight: '$medium', textAlign: 'left', }), @@ -106,10 +105,10 @@ const styles = { gap: '4px 0', }), numberLabel: cssObj({ - backgroundColor: '$white', + backgroundColor: '$gray1', borderRadius: '$full', - color: '#202020', - border: '1.5px solid #8D8D8D', + color: '$gray12', + border: '1.5px solid $gray8', width: '20px', height: '20px', display: 'flex', From 02bb118fb8f27d324dfa641238bdb403cd90344b Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Thu, 30 Jan 2025 14:57:37 -0300 Subject: [PATCH 48/85] refactor: remove unused transaction display components --- .../operations/TxAddressDisplay.tsx | 94 ------------------- .../operations/TxAssetAmount.tsx | 76 --------------- .../operations/TxAssetDisplay.tsx | 45 --------- 3 files changed, 215 deletions(-) delete mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetAmount.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetDisplay.tsx diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx deleted file mode 100644 index 21c0220c8b..0000000000 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAddressDisplay.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { Avatar, Box, IconButton, Text } from '@fuel-ui/react'; -import { useAccounts } from '~/systems/Account'; -import { shortAddress } from '~/systems/Core'; - -type TxAddressDisplayProps = { - address: string; - name?: string; - image?: string; - isContract?: boolean; - label?: string; -}; - -export function TxAddressDisplay({ - address, - name, - image, - isContract, -}: TxAddressDisplayProps) { - const { accounts } = useAccounts(); - const account = accounts?.find( - (acc) => acc.address.toLowerCase() === address.toLowerCase() - ); - - return ( - - - {image ? ( - - ) : ( - - )} - - - - {account?.name || 'Unknown'} - - {isContract && ( - - - Contract - - - )} - - {shortAddress(address)} - - navigator.clipboard.writeText(address)} - /> - - - ); -} - -const styles = { - root: cssObj({ - display: 'flex', - alignItems: 'center', - gap: '$1', - }), - iconCol: cssObj({ - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - flexShrink: 0, - }), - contentCol: cssObj({ - display: 'flex', - flex: 1, - }), - badge: cssObj({ - padding: '0 $1', - backgroundColor: '$gray3', - borderRadius: '$md', - }), - name: cssObj({ - fontWeight: '$semibold', - color: '#202020', - }), - address: cssObj({ - fontWeight: '$medium', - color: '#646464', - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetAmount.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetAmount.tsx deleted file mode 100644 index 3f14d57a28..0000000000 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetAmount.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { Box, Icon, Text } from '@fuel-ui/react'; -import { useAsset } from '~/systems/Asset'; -import { formatAmount, shortAddress } from '~/systems/Core'; - -type TxAssetAmountProps = { - amount: string; - assetId?: string; - showIcon?: boolean; - showSymbol?: boolean; - showAssetId?: boolean; - showLabel?: boolean; - label?: string; -}; - -export function TxAssetAmount({ - amount, - assetId, - showIcon = true, - showSymbol = true, - showAssetId = true, - showLabel = true, - label, -}: TxAssetAmountProps) { - const asset = useAsset(assetId); - - return ( - - {showLabel && label && ( - - {label} - - )} - - {showIcon && } - - {formatAmount({ - amount, - options: { units: 9 }, // Default to 9 decimals for ETH - })} - {showSymbol && asset && ( - - {' '} - {asset.symbol || 'Unknown'} - - )} - {showAssetId && assetId && ( - - {' '} - ({shortAddress(assetId)}) - - )} - - - - ); -} - -const styles = { - root: cssObj({ - display: 'flex', - flexDirection: 'column', - }), - content: cssObj({ - display: 'flex', - gap: '$1', - alignItems: 'center', - }), - symbol: cssObj({ - fontWeight: '$semibold', - }), - assetId: cssObj({ - color: '$gray8', - fontSize: '$xs', - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetDisplay.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetDisplay.tsx deleted file mode 100644 index 7f7efaf073..0000000000 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxAssetDisplay.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { Box } from '@fuel-ui/react'; -import { TxAssetAmount } from './TxAssetAmount'; - -type TxAssetDisplayProps = { - amount: string; - assetId?: string; - label?: string; - operationCount?: number; - showLabel?: boolean; -}; - -export function TxAssetDisplay({ - amount, - assetId, - label, - operationCount, - showLabel, -}: TxAssetDisplayProps) { - return ( - - - {operationCount && operationCount > 1 && ( - x{operationCount} - )} - - ); -} - -const styles = { - root: cssObj({ - display: 'flex', - flexDirection: 'column', - }), - count: cssObj({ - color: '$gray8', - fontSize: '$xs', - marginLeft: '$1', - }), -}; From 9ffd5d19038126131a24fd0a9e959e9e9ef02014 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Thu, 30 Jan 2025 14:57:53 -0300 Subject: [PATCH 49/85] style: improve TxOperation asset rendering and color tokens --- .../operations/TxOperation.tsx | 72 +++++++++---------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx index 3b31e9d4c3..ad2693c08d 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx @@ -5,8 +5,8 @@ import { Box, Icon, IconButton, + Image, Text, - Tooltip, } from '@fuel-ui/react'; import type { AssetFuelAmount } from '@fuel-wallet/types'; import { bn } from 'fuels'; @@ -31,6 +31,27 @@ const renderAssets = (amounts: AssetFuelAmount[]) => { if (allEmptyAmounts) return null; + const getAssetImage = (asset: AssetFuelAmount) => { + if (asset?.icon) { + return ( + {`${asset.name} + ); + } + + return ( + + ); + }; + return ( @@ -38,29 +59,17 @@ const renderAssets = (amounts: AssetFuelAmount[]) => { (assetAmount) => bn(assetAmount.amount).gt(0) && ( - + {getAssetImage(assetAmount)} - + {formatAmount({ amount: assetAmount.amount, options: { units: assetAmount.decimals || 0, precision: assetAmount.decimals || 0, }, })} - delayDuration={0} - open={false} - > - - {formatAmount({ - amount: assetAmount.amount, - options: { - units: assetAmount.decimals || 0, - precision: assetAmount.decimals || 0, - }, - })} - - + {assetAmount.symbol} {assetAmount.isNft && ( - {/* From Address */} Date: Thu, 30 Jan 2025 15:43:10 -0300 Subject: [PATCH 50/85] refactor: simplify TxHeaderSimple component and remove unused props --- .../TxViewSimple/TxHeaderSimple.tsx | 66 ++----------------- .../components/TxViewSimple/TxViewSimple.tsx | 4 +- 2 files changed, 8 insertions(+), 62 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx index 85e2b1ab03..8d8e0846dc 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx @@ -1,20 +1,7 @@ import { cssObj } from '@fuel-ui/css'; -import { Box, Card, ContentLoader, Icon, Text } from '@fuel-ui/react'; -import type { TransactionStatus } from 'fuels'; - -type TxHeaderSimpleProps = { - status?: TransactionStatus; - origin?: { - name: string; - favicon?: string; - url?: string; - }; - isLoading?: boolean; -}; - -export function TxHeaderSimple({ isLoading }: TxHeaderSimpleProps) { - if (isLoading) return ; +import { Box, Icon } from '@fuel-ui/react'; +export function TxHeaderSimple() { return ( {/* Disabled while the new Wallet header is not implemented */} @@ -27,61 +14,20 @@ export function TxHeaderSimple({ isLoading }: TxHeaderSimpleProps) { ); } -TxHeaderSimple.Loader = function TxHeaderSimpleLoader() { - return ( - - - - - - ); -}; - const styles = { header: cssObj({ - display: 'flex', - flexDirection: 'column', - gap: '$2', - marginBottom: '$1', + marginBottom: '$2', backgroundColor: '$white', - borderBottom: '1px solid $border', + borderBottom: '1px solid $gray3', padding: '12px 18px', }), warning: cssObj({ display: 'flex', alignItems: 'center', - gap: '$2', + gap: '$1', fontSize: '12px', - color: '#646464', + color: '$gray11', fontWeight: '500', marginBottom: '$2', }), - root: cssObj({ - padding: '$3', - }), - content: cssObj({ - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - }), - origin: cssObj({ - display: 'flex', - alignItems: 'center', - gap: '$2', - }), - favicon: cssObj({ - width: '20px', - height: '20px', - borderRadius: '$md', - }), - icon: cssObj({ - width: '20px', - height: '20px', - color: '#8d8d8d', - }), - status: cssObj({ - display: 'flex', - alignItems: 'center', - gap: '$2', - }), }; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx index c48231d2f2..97101a92ca 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx @@ -21,7 +21,7 @@ export function TxViewSimple({ return ( - + - + From 78697f2825f075c904823026b2332fd4cafffa63 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Thu, 30 Jan 2025 16:11:30 -0300 Subject: [PATCH 51/85] style: improve TxFeeOptionsSimple input styling and focus states --- .../TxViewSimple/TxFeeOptionsSimple.tsx | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx index f34166bb63..46802db6f7 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx @@ -45,18 +45,13 @@ export function TxFeeOptionsSimple({ Gas limit - - + + Tip - + handleTipChange(e.target.value)} /> @@ -105,6 +99,19 @@ const styles = { flexDirection: 'column', gap: '$2', }), + input: cssObj({ + borderRadius: '8px', + backgroundColor: '$white', + border: '1px solid $gray5', + transition: 'border-color 0.3s', + '&:not([aria-disabled="true"]):focus-within': { + borderColor: '$brand', + }, + + '&>input': { + width: '100%', + }, + }), backButton: cssObj({ alignSelf: 'center', color: '$accent11', From 79a992cfc9e8e90fb928312fdd83a546526b28ce Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Thu, 30 Jan 2025 20:13:17 -0300 Subject: [PATCH 52/85] feat: add categorized operations to transaction view --- .../TxOperationsSimple/TxOperationsList.tsx | 57 ++----------------- .../components/TxViewSimple/TxViewSimple.tsx | 7 +-- .../app/src/systems/Transaction/types.tsx | 9 ++- .../Transaction/utils/simplifyTransaction.ts | 51 ++++++++++++++++- 4 files changed, 65 insertions(+), 59 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx index 384623134a..34f459292c 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx @@ -1,62 +1,15 @@ import { Box } from '@fuel-ui/react'; -import { useMemo } from 'react'; -import type { SimplifiedOperation } from '../../../types'; -import { TxCategory } from '../../../types'; +import type { CategorizedOperations } from '../../../types'; import { TxOperationsGroup } from './TxOperationsGroup'; import { TxOperation } from './operations/TxOperation'; type TxOperationsListProps = { - operations: SimplifiedOperation[]; - currentAccount?: string; + operations: CategorizedOperations; }; -export function TxOperationsList({ - operations, - currentAccount, -}: TxOperationsListProps) { +export function TxOperationsList({ operations }: TxOperationsListProps) { const { mainOperations, otherRootOperations, intermediateOperations } = - useMemo(() => { - const main: SimplifiedOperation[] = []; - const otherRoot: SimplifiedOperation[] = []; - const intermediate: SimplifiedOperation[] = []; - - for (const op of operations) { - const depth = op.metadata?.depth || 0; - const isTransfer = op.type === TxCategory.SEND; - const isFromCurrentAccount = - currentAccount && - op.from.toLowerCase() === currentAccount.toLowerCase(); - const isToCurrentAccount = - currentAccount && - op.to.toLowerCase() === currentAccount.toLowerCase(); - - // All transfers go to main list - if (isTransfer) { - main.push(op); - continue; - } - - // Contract calls at root level (depth 0) - if (depth === 0) { - // If related to current account, show in main list - if (isFromCurrentAccount || isToCurrentAccount) { - main.push(op); - } else { - otherRoot.push(op); - } - continue; - } - - // All other operations (intermediate contract calls) - intermediate.push(op); - } - - return { - mainOperations: main, - otherRootOperations: otherRoot, - intermediateOperations: intermediate, - }; - }, [operations, currentAccount]); + operations; return ( @@ -65,7 +18,7 @@ export function TxOperationsList({ (); - const { account } = useAccounts(); + // const { account } = useAccounts(); return ( - + {showDetails && ( diff --git a/packages/app/src/systems/Transaction/types.tsx b/packages/app/src/systems/Transaction/types.tsx index 3f4e956309..aa3b6b026b 100644 --- a/packages/app/src/systems/Transaction/types.tsx +++ b/packages/app/src/systems/Transaction/types.tsx @@ -94,9 +94,16 @@ export type SimplifiedFee = { gasPrice?: BN; }; +export type CategorizedOperations = { + mainOperations: SimplifiedOperation[]; + otherRootOperations: SimplifiedOperation[]; + intermediateOperations: SimplifiedOperation[]; +}; + export type SimplifiedTransaction = { - id?: string; + id: string; operations: SimplifiedOperation[]; + categorizedOperations: CategorizedOperations; timestamp?: Date; fee: SimplifiedFee; origin?: { diff --git a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts index 86ac855d28..b26141a76a 100644 --- a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts +++ b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts @@ -8,6 +8,7 @@ import { } from 'fuels'; import type { ContractCallMetadata, SimplifiedOperation } from '../types'; import { TxCategory } from '../types'; +import type { CategorizedOperations } from '../types'; import type { SimplifiedTransaction } from '../types.tsx'; type TransactionRequestWithOrigin = TransactionRequest & { @@ -145,13 +146,60 @@ export function transformOperations( return operations; } +function categorizeOperations( + operations: SimplifiedOperation[], + currentAccount?: string +): CategorizedOperations { + const main: SimplifiedOperation[] = []; + const otherRoot: SimplifiedOperation[] = []; + const intermediate: SimplifiedOperation[] = []; + + for (const op of operations) { + const depth = op.metadata?.depth || 0; + const isTransfer = op.type === TxCategory.SEND; + const isFromCurrentAccount = + currentAccount && op.from.toLowerCase() === currentAccount.toLowerCase(); + const isToCurrentAccount = + currentAccount && op.to.toLowerCase() === currentAccount.toLowerCase(); + + // All transfers go to main list + if (isTransfer) { + main.push(op); + continue; + } + + // Contract calls at root level (depth 0) + if (depth === 0) { + // If related to current account, show in main list + if (isFromCurrentAccount || isToCurrentAccount) { + main.push(op); + } else { + otherRoot.push(op); + } + continue; + } + + // All other operations (intermediate contract calls) + intermediate.push(op); + } + + return { + mainOperations: main, + otherRootOperations: otherRoot, + intermediateOperations: intermediate, + }; +} + export function simplifyTransaction( summary: TransactionSummary, request?: TransactionRequest, currentAccount?: string ): SimplifiedTransaction { - console.log('summary', summary); const operations = transformOperations(summary, currentAccount); + const categorizedOperations = categorizeOperations( + operations, + currentAccount + ); const requestWithOrigin = request as TransactionRequestWithOrigin; const origin = requestWithOrigin?.origin; @@ -160,6 +208,7 @@ export function simplifyTransaction( return { id: summary.id, operations, + categorizedOperations, timestamp: summary.time ? new Date(summary.time) : undefined, fee: { total: new BN(summary.fee || 0), From 3713e55d43978a462082b9658c729a776332394f Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Thu, 30 Jan 2025 20:13:31 -0300 Subject: [PATCH 53/85] feat: improve contract operation type detection --- .../TxOperationsSimple/operations/TxOperation.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx index ad2693c08d..3c21803d81 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx @@ -167,7 +167,12 @@ export function TxOperation({ }, [operation]); const getOperationType = () => { - if (isContract) return 'Calls contract (sending funds)'; + if (isContract) { + if (operation.metadata?.amount && operation.metadata?.assetId) { + return 'Calls contract (sending funds)'; + } + return 'Calls contract'; + } if (isTransfer) return 'Sends token'; return 'Unknown'; }; From 875d2c59594f857d2a297f2ad8983a10c068ce21 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Thu, 30 Jan 2025 21:27:57 -0300 Subject: [PATCH 54/85] feat: add SimplifiedAddress type for enhanced transaction address handling --- .../operations/TxOperation.tsx | 35 +++++++++++-------- .../app/src/systems/Transaction/types.tsx | 9 +++-- .../Transaction/utils/simplifyTransaction.ts | 25 ++++++++----- 3 files changed, 44 insertions(+), 25 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx index 3c21803d81..c9a59d3813 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx @@ -156,10 +156,10 @@ export function TxOperation({ const [assetsAmount, setAssetsAmount] = useState([]); const accountFrom = accounts?.find( - (acc) => acc.address.toLowerCase() === operation.from.toLowerCase() + (acc) => acc.address.toLowerCase() === operation.from.address.toLowerCase() ); const accountTo = accounts?.find( - (acc) => acc.address.toLowerCase() === operation.to.toLowerCase() + (acc) => acc.address.toLowerCase() === operation.to.address.toLowerCase() ); useEffect(() => { @@ -185,6 +185,9 @@ export function TxOperation({ (operation.amount && operation.assetId) || (metadata?.amount && metadata?.assetId); + const isFromContract = operation.from.type === 0; + const isToContract = operation.to.type === 0; + return ( - + {accountFrom?.name || 'Unknown'} - {isContract && ( + {isFromContract && ( Contract @@ -218,14 +221,16 @@ export function TxOperation({ )} - {shortAddress(operation.from)} + {shortAddress(operation.from.address)} navigator.clipboard.writeText(operation.from)} + onPress={() => + navigator.clipboard.writeText(operation.from.address) + } /> @@ -256,30 +261,30 @@ export function TxOperation({ {accountTo?.name || 'Unknown'} - {isContract && ( + {isToContract && ( - + Contract )} - {shortAddress(operation.to)} + {shortAddress(operation.to.address)} navigator.clipboard.writeText(operation.to)} + onPress={() => navigator.clipboard.writeText(operation.to.address)} /> @@ -317,7 +322,7 @@ const styles = { padding: '2px 0', }), badge: cssObj({ - padding: '0 $1', + padding: '2px $1', backgroundColor: '$gray3', borderRadius: '$md', }), diff --git a/packages/app/src/systems/Transaction/types.tsx b/packages/app/src/systems/Transaction/types.tsx index aa3b6b026b..7dc91b2301 100644 --- a/packages/app/src/systems/Transaction/types.tsx +++ b/packages/app/src/systems/Transaction/types.tsx @@ -75,10 +75,15 @@ export type SwapMetadata = { parentReceiptId?: string; }; +export type SimplifiedAddress = { + address: string; + type: number; // 0 for contract, 1 for account +}; + export type SimplifiedOperation = { type: TxCategory; - from: string; - to: string; + from: SimplifiedAddress; + to: SimplifiedAddress; amount?: BN; assetId?: string; isFromCurrentAccount: boolean; diff --git a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts index b26141a76a..00f1c4909b 100644 --- a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts +++ b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts @@ -66,6 +66,7 @@ function transformOperation( calls = [], receipts = [], } = operation; + const type = getOperationType(operation); const receipt = receipts[0]; const depth = receipt ? getReceiptDepth(receipt, allReceipts) : 0; @@ -74,6 +75,11 @@ function transformOperation( ? from?.address === currentAccount : false; + const fromAddress = from + ? { address: from.address, type: from.type } + : undefined; + const toAddress = to ? { address: to.address, type: to.type } : undefined; + if (name === OperationName.contractCall && calls.length > 0) { const call = calls[0] as OperationFunctionCall; const metadata: ContractCallMetadata = { @@ -88,8 +94,8 @@ function transformOperation( return { type, - from: from?.address || '', - to: to?.address || '', + from: fromAddress!, + to: toAddress!, isFromCurrentAccount, metadata, }; @@ -99,8 +105,8 @@ function transformOperation( const asset = assetsSent[0]; return { type, - from: from?.address || '', - to: to?.address || '', + from: fromAddress!, + to: toAddress!, isFromCurrentAccount, amount: new BN(asset.amount), assetId: asset.assetId, @@ -112,8 +118,8 @@ function transformOperation( return { type, - from: from?.address || '', - to: to?.address || '', + from: fromAddress!, + to: toAddress!, isFromCurrentAccount, metadata: { depth, @@ -158,9 +164,11 @@ function categorizeOperations( const depth = op.metadata?.depth || 0; const isTransfer = op.type === TxCategory.SEND; const isFromCurrentAccount = - currentAccount && op.from.toLowerCase() === currentAccount.toLowerCase(); + currentAccount && + op.from.address.toLowerCase() === currentAccount.toLowerCase(); const isToCurrentAccount = - currentAccount && op.to.toLowerCase() === currentAccount.toLowerCase(); + currentAccount && + op.to.address.toLowerCase() === currentAccount.toLowerCase(); // All transfers go to main list if (isTransfer) { @@ -195,6 +203,7 @@ export function simplifyTransaction( request?: TransactionRequest, currentAccount?: string ): SimplifiedTransaction { + console.log('summary', summary); const operations = transformOperations(summary, currentAccount); const categorizedOperations = categorizeOperations( operations, From 298f5404562083a0b7157723cac7a09f5c320b46 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Thu, 30 Jan 2025 22:21:40 -0300 Subject: [PATCH 55/85] refactor: improve transaction operation depth calculation and receipt handling --- .../Transaction/utils/simplifyTransaction.ts | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts index 00f1c4909b..0ffa48bb76 100644 --- a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts +++ b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts @@ -32,22 +32,16 @@ function getOperationType(operation: Operation): TxCategory { } function getReceiptDepth( - receipt: TransactionResultReceipt, - allReceipts: TransactionResultReceipt[] + allReceipts: TransactionResultReceipt[], + receiptIndex: number ): number { - const receiptIndex = allReceipts.findIndex((r) => r === receipt); - if (receiptIndex === -1) return 0; - let depth = 0; + for (let i = 0; i < receiptIndex; i++) { const r = allReceipts[i]; if (r.type === ReceiptType.Call) depth++; if (r.type === ReceiptType.ReturnData && depth > 0) depth--; - if ( - r.type === ReceiptType.ScriptResult && - allReceipts[i - 1]?.type !== ReceiptType.Return - ) - depth = 0; + if (r.type === ReceiptType.ScriptResult) depth = 0; } return depth; @@ -56,7 +50,8 @@ function getReceiptDepth( function transformOperation( operation: Operation, allReceipts: TransactionResultReceipt[], - currentAccount?: string + currentAccount?: string, + receiptIndex?: number ): SimplifiedOperation { const { name, @@ -69,7 +64,11 @@ function transformOperation( const type = getOperationType(operation); const receipt = receipts[0]; - const depth = receipt ? getReceiptDepth(receipt, allReceipts) : 0; + + const depth = + receipt && typeof receiptIndex === 'number' + ? getReceiptDepth(allReceipts, receiptIndex) + : 0; const isFromCurrentAccount = currentAccount ? from?.address === currentAccount @@ -133,22 +132,31 @@ export function transformOperations( ): SimplifiedOperation[] { if (!summary.operations) return []; - // Get all receipts from all operations - const allReceipts = summary.operations.flatMap((op) => op.receipts || []); + const allReceipts = summary.receipts || []; - console.log('All receipts:', allReceipts); + const operations = summary.operations.map((op) => { + const operationReceipt = op.receipts?.[0]; + if (!operationReceipt) return transformOperation(op, [], currentAccount); - // Transform operations with receipt depth information - const operations = summary.operations.map((op) => - transformOperation(op, allReceipts, currentAccount) - ); + const receiptIndex = allReceipts.findIndex( + (r) => + r.type === operationReceipt.type && + r.pc === operationReceipt.pc && + r.is === operationReceipt.is + ); + + if (receiptIndex === -1) { + console.warn('Could not find operation receipt in full receipt list'); + return transformOperation(op, [], currentAccount); + } + + return transformOperation(op, allReceipts, currentAccount, receiptIndex); + }); - // Sort by depth to maintain visual hierarchy operations.sort( (a, b) => (a.metadata?.depth || 0) - (b.metadata?.depth || 0) ); - console.log('Transformed operations with depth:', operations); return operations; } @@ -170,15 +178,12 @@ function categorizeOperations( currentAccount && op.to.address.toLowerCase() === currentAccount.toLowerCase(); - // All transfers go to main list if (isTransfer) { main.push(op); continue; } - // Contract calls at root level (depth 0) if (depth === 0) { - // If related to current account, show in main list if (isFromCurrentAccount || isToCurrentAccount) { main.push(op); } else { @@ -187,7 +192,6 @@ function categorizeOperations( continue; } - // All other operations (intermediate contract calls) intermediate.push(op); } From 65a605ff242090d3feaddf38a2feff6135ec3336 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Thu, 30 Jan 2025 23:25:48 -0300 Subject: [PATCH 56/85] base goruping --- .../operations/TxOperation.tsx | 240 +++++++++++------- .../app/src/systems/Transaction/types.tsx | 10 + .../Transaction/utils/simplifyTransaction.ts | 61 ++++- 3 files changed, 214 insertions(+), 97 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx index c9a59d3813..b3dbf1a429 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx @@ -162,6 +162,8 @@ export function TxOperation({ (acc) => acc.address.toLowerCase() === operation.to.address.toLowerCase() ); + const [isExpanded, setIsExpanded] = useState(false); + useEffect(() => { fetchAssetsAmount(operation).then(setAssetsAmount); }, [operation]); @@ -189,106 +191,142 @@ export function TxOperation({ const isToContract = operation.to.type === 0; return ( - - - - - - - - {accountFrom?.name || 'Unknown'} - - {isFromContract && ( - - - Contract - - - )} - - {shortAddress(operation.from.address)} - - - navigator.clipboard.writeText(operation.from.address) - } - /> - + + + + + + + + + {accountFrom?.name || 'Unknown'} + + {isFromContract && ( + + + Contract + + + )} + + {shortAddress(operation.from.address)} + + + navigator.clipboard.writeText(operation.from.address) + } + /> + - {/* Spacer and Arrow */} - - - - - - - - - {getOperationType()} - + {/* Spacer and Arrow */} + + + + + + + + + {getOperationType()} + - {/* Asset Amount */} - - - - - {shouldShowAssetAmount && - assetsAmount.length > 0 && - renderAssets(assetsAmount)} - + {/* Asset Amount */} + + + + + {shouldShowAssetAmount && + assetsAmount.length > 0 && + renderAssets(assetsAmount)} + - {/* To Address */} - - + {/* To Address */} + + + + + + {accountTo?.name || 'Unknown'} + + {isToContract && ( + + + Contract + + + )} + + {shortAddress(operation.to.address)} + + + navigator.clipboard.writeText(operation.to.address) + } + /> + - - - {accountTo?.name || 'Unknown'} + + {metadata.operationCount && ( + setIsExpanded(!isExpanded)} + > + + + {isExpanded ? 'Collapse' : 'Expand'} - {isToContract && ( - - - Contract - - + {isExpanded ? null : ( + + (+{metadata.operationCount} operations) + )} - - {shortAddress(operation.to.address)} - - navigator.clipboard.writeText(operation.to.address)} - /> - - - + + )} + {isExpanded && ( + +
{JSON.stringify(metadata.groupedAssets)}
+
+ )} + ); } @@ -350,4 +388,18 @@ const styles = { overflow: 'hidden', textOverflow: 'ellipsis', }, + operationCount: { + marginTop: '$2', + marginLeft: '$2', + display: 'flex', + alignItems: 'center', + gap: '$1', + justifyContent: 'center', + marginBottom: '$2', + cursor: 'pointer', + }, + expandedOperations: cssObj({ + marginLeft: '$2', + marginTop: '$2', + }), }; diff --git a/packages/app/src/systems/Transaction/types.tsx b/packages/app/src/systems/Transaction/types.tsx index 7dc91b2301..5064e99660 100644 --- a/packages/app/src/systems/Transaction/types.tsx +++ b/packages/app/src/systems/Transaction/types.tsx @@ -53,6 +53,14 @@ export enum OperationDirection { unknown = 'Unknown', } +export type GroupedAssets = { + [assetId: string]: { + amount: BN; + assetId: string; + assetAmount?: AssetFuelAmount; + }; +}; + export type ContractCallMetadata = { contractId?: string; functionName?: string; @@ -62,6 +70,8 @@ export type ContractCallMetadata = { depth?: number; receiptType?: ReceiptType; assetAmount?: AssetFuelAmount; + operationCount?: number; + groupedAssets?: GroupedAssets; }; export type SwapMetadata = { diff --git a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts index 0ffa48bb76..497094d155 100644 --- a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts +++ b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts @@ -160,6 +160,60 @@ export function transformOperations( return operations; } +function groupSimilarOperations( + operations: SimplifiedOperation[] +): SimplifiedOperation[] { + const groupedOps = new Map(); + + for (const op of operations) { + const key = [ + op.type, + op.from.address, + op.to.address, + op.metadata.functionName || '', + ].join('|'); + + const existing = groupedOps.get(key); + if (!existing) { + const newOp = { + ...op, + metadata: { + ...op.metadata, + operationCount: 1, + groupedAssets: + op.amount && op.assetId + ? { + [op.assetId]: { + amount: op.amount, + assetId: op.assetId, + assetAmount: op.assetAmount, + }, + } + : undefined, + }, + }; + groupedOps.set(key, newOp); + continue; + } + + const groupedAssets = existing.metadata.groupedAssets || {}; + if (op.amount && op.assetId) { + const existingAsset = groupedAssets[op.assetId]; + groupedAssets[op.assetId] = { + amount: existingAsset ? existingAsset.amount.add(op.amount) : op.amount, + assetId: op.assetId, + assetAmount: op.assetAmount, + }; + } + + existing.metadata.operationCount = + (existing.metadata.operationCount || 1) + 1; + existing.metadata.groupedAssets = groupedAssets; + } + + return Array.from(groupedOps.values()); +} + function categorizeOperations( operations: SimplifiedOperation[], currentAccount?: string @@ -195,10 +249,11 @@ function categorizeOperations( intermediate.push(op); } + // Group similar operations in each category return { - mainOperations: main, - otherRootOperations: otherRoot, - intermediateOperations: intermediate, + mainOperations: groupSimilarOperations(main), + otherRootOperations: groupSimilarOperations(otherRoot), + intermediateOperations: groupSimilarOperations(intermediate), }; } From 3f5173b0d32d09c52066ddf84e0a58b0dbfe02a6 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Fri, 31 Jan 2025 11:17:02 -0300 Subject: [PATCH 57/85] Separate operation card. --- .../operations/TxOperation.tsx | 284 +----------------- .../operations/TxOperationCard.tsx | 284 ++++++++++++++++++ 2 files changed, 299 insertions(+), 269 deletions(-) create mode 100644 packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationCard.tsx diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx index b3dbf1a429..a1d0953fcd 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx @@ -1,94 +1,18 @@ import { cssObj } from '@fuel-ui/css'; -import { - Avatar, - Badge, - Box, - Icon, - IconButton, - Image, - Text, -} from '@fuel-ui/react'; +import { Box, Icon, Text } from '@fuel-ui/react'; import type { AssetFuelAmount } from '@fuel-wallet/types'; -import { bn } from 'fuels'; import { useEffect, useState } from 'react'; -import { useAccounts } from '~/systems/Account'; import { AssetsCache } from '~/systems/Asset/cache/AssetsCache'; -import { shortAddress } from '~/systems/Core'; -import { formatAmount } from '~/systems/Core'; import { NetworkService } from '~/systems/Network/services/network'; import { TxCategory } from '../../../../types'; import type { SimplifiedOperation } from '../../../../types'; +import { TxOperationCard } from './TxOperationCard'; type TxOperationProps = { operation: SimplifiedOperation; showNesting?: boolean; }; -const renderAssets = (amounts: AssetFuelAmount[]) => { - const allEmptyAmounts = amounts.every((assetAmount) => - bn(assetAmount.amount).eq(0) - ); - - if (allEmptyAmounts) return null; - - const getAssetImage = (asset: AssetFuelAmount) => { - if (asset?.icon) { - return ( - {`${asset.name} - ); - } - - return ( - - ); - }; - - return ( - - - {amounts.map( - (assetAmount) => - bn(assetAmount.amount).gt(0) && ( - - {getAssetImage(assetAmount)} - - - {formatAmount({ - amount: assetAmount.amount, - options: { - units: assetAmount.decimals || 0, - precision: assetAmount.decimals || 0, - }, - })} - - {assetAmount.symbol} - {assetAmount.isNft && ( - - NFT - - )} - - - ) - )} - - - ); -}; - const fetchAssetsAmount = async (operation: SimplifiedOperation) => { try { const coins = []; @@ -150,159 +74,21 @@ export function TxOperation({ }: TxOperationProps) { const metadata = operation.metadata; const isContract = operation.type === TxCategory.CONTRACTCALL; - const isTransfer = operation.type === TxCategory.SEND; const depth = metadata?.depth || 0; - const { accounts } = useAccounts(); + console.log(showNesting, isContract, depth); const [assetsAmount, setAssetsAmount] = useState([]); - - const accountFrom = accounts?.find( - (acc) => acc.address.toLowerCase() === operation.from.address.toLowerCase() - ); - const accountTo = accounts?.find( - (acc) => acc.address.toLowerCase() === operation.to.address.toLowerCase() - ); - const [isExpanded, setIsExpanded] = useState(false); - + console.log(operation); useEffect(() => { fetchAssetsAmount(operation).then(setAssetsAmount); }, [operation]); - const getOperationType = () => { - if (isContract) { - if (operation.metadata?.amount && operation.metadata?.assetId) { - return 'Calls contract (sending funds)'; - } - return 'Calls contract'; - } - if (isTransfer) return 'Sends token'; - return 'Unknown'; - }; - - // For transfers, always show with 0 indentation - // For contract calls, only show if root level (depth === 0) unless showNesting is true - if (isContract && !showNesting && depth !== 0) return null; - - const shouldShowAssetAmount = - (operation.amount && operation.assetId) || - (metadata?.amount && metadata?.assetId); - - const isFromContract = operation.from.type === 0; - const isToContract = operation.to.type === 0; + // if (isContract && !showNesting && depth !== 0) return null; return ( - - - - - - - - {accountFrom?.name || 'Unknown'} - - {isFromContract && ( - - - Contract - - - )} - - {shortAddress(operation.from.address)} - - - navigator.clipboard.writeText(operation.from.address) - } - /> - - - {/* Spacer and Arrow */} - - - - - - - - - {getOperationType()} - - - {/* Asset Amount */} - - - - - {shouldShowAssetAmount && - assetsAmount.length > 0 && - renderAssets(assetsAmount)} - - - {/* To Address */} - - - - - - {accountTo?.name || 'Unknown'} - - {isToContract && ( - - - Contract - - - )} - - {shortAddress(operation.to.address)} - - - navigator.clipboard.writeText(operation.to.address) - } - /> - - - - {metadata.operationCount && ( + + {metadata.operationCount && metadata.operationCount > 1 && ( setIsExpanded(!isExpanded)} @@ -323,7 +109,14 @@ export function TxOperation({ )} {isExpanded && ( -
{JSON.stringify(metadata.groupedAssets)}
+ {/* Show individual operations */} + {metadata.groupedAssets?.map((op, idx) => ( + + ))}
)}
@@ -341,53 +134,6 @@ const styles = { padding: '14px 12px', margin: '0 4px', }), - blue: cssObj({ - fontSize: '$sm', - display: 'flex', - alignItems: 'center', - gap: '$1', - color: '$indigo11', - lineHeight: 'normal', - }), - spacer: cssObj({ - minHeight: '14px', - width: '2px', - height: '100%', - backgroundColor: '$gray6', - borderRadius: '$lg', - }), - iconCol: cssObj({ - padding: '2px 0', - }), - badge: cssObj({ - padding: '2px $1', - backgroundColor: '$gray3', - borderRadius: '$md', - }), - name: cssObj({ - fontWeight: '$semibold', - color: '$gray12', - }), - address: cssObj({ - fontWeight: '$medium', - color: '$gray11', - }), - asset: { - alignItems: 'center', - gap: '$2', - marginTop: '$1', - }, - assetNft: { - padding: '$1 $2', - }, - amountContainer: { - fontWeight: '$semibold', - color: '$gray12', - fontSize: '$sm', - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - }, operationCount: { marginTop: '$2', marginLeft: '$2', diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationCard.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationCard.tsx new file mode 100644 index 0000000000..29d6dfaf4c --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationCard.tsx @@ -0,0 +1,284 @@ +import { cssObj } from '@fuel-ui/css'; +import { + Avatar, + Badge, + Box, + Icon, + IconButton, + Image, + Text, +} from '@fuel-ui/react'; +import type { AssetFuelAmount } from '@fuel-wallet/types'; +import { bn } from 'fuels'; +import { useAccounts } from '~/systems/Account'; +import { formatAmount, shortAddress } from '~/systems/Core'; +import type { SimplifiedOperation } from '../../../../types'; +import { TxCategory } from '../../../../types'; + +type TxOperationCardProps = { + operation: SimplifiedOperation; + assetsAmount: AssetFuelAmount[]; +}; + +export function TxOperationCard({ + operation, + assetsAmount, +}: TxOperationCardProps) { + const { accounts } = useAccounts(); + const isContract = operation.type === TxCategory.CONTRACTCALL; + const isTransfer = operation.type === TxCategory.SEND; + + const accountFrom = accounts?.find( + (acc) => acc.address.toLowerCase() === operation.from.address.toLowerCase() + ); + const accountTo = accounts?.find( + (acc) => acc.address.toLowerCase() === operation.to.address.toLowerCase() + ); + + const getOperationType = () => { + if (isContract) { + if (operation.metadata?.amount && operation.metadata?.assetId) { + return 'Calls contract (sending funds)'; + } + return 'Calls contract'; + } + if (isTransfer) return 'Sends token'; + return 'Unknown'; + }; + + const shouldShowAssetAmount = + (operation.amount && operation.assetId) || + (operation.metadata?.amount && operation.metadata?.assetId); + + const isFromContract = operation.from.type === 0; + const isToContract = operation.to.type === 0; + + const renderAssets = (amounts: AssetFuelAmount[]) => { + const allEmptyAmounts = amounts.every((assetAmount) => + bn(assetAmount.amount).eq(0) + ); + + if (allEmptyAmounts) return null; + + const getAssetImage = (asset: AssetFuelAmount) => { + if (asset?.icon) { + return ( + {`${asset.name} + ); + } + + return ( + + ); + }; + + return ( + + + {amounts.map( + (assetAmount) => + bn(assetAmount.amount).gt(0) && ( + + {getAssetImage(assetAmount)} + + + {formatAmount({ + amount: assetAmount.amount, + options: { + units: assetAmount.decimals || 0, + precision: assetAmount.decimals || 0, + }, + })} + + {assetAmount.symbol} + {assetAmount.isNft && ( + + NFT + + )} + + + ) + )} + + + ); + }; + + return ( + + + {/* From Address */} + + + + + + {accountFrom?.name || 'Unknown'} + + {isFromContract && ( + + + Contract + + + )} + + {shortAddress(operation.from.address)} + + + navigator.clipboard.writeText(operation.from.address) + } + /> + + + {/* Spacer and Arrow */} + + + + + + + + + {getOperationType()} + + + {/* Asset Amount */} + + + + + {shouldShowAssetAmount && + assetsAmount.length > 0 && + renderAssets(assetsAmount)} + + + {/* To Address */} + + + + + + {accountTo?.name || 'Unknown'} + + {isToContract && ( + + + Contract + + + )} + + {shortAddress(operation.to.address)} + + navigator.clipboard.writeText(operation.to.address)} + /> + + + + ); +} + +const styles = { + contentCol: cssObj({ + display: 'flex', + backgroundColor: '$gray1', + boxShadow: '0px 2px 6px -1px $colors$gray4, 0px 0px 0px 1px $colors$gray6', + flex: 1, + borderRadius: '8px', + minWidth: 0, + padding: '14px 12px', + margin: '0 4px', + }), + spacer: cssObj({ + minHeight: '14px', + width: '2px', + height: '100%', + backgroundColor: '$gray6', + borderRadius: '$lg', + }), + iconCol: cssObj({ + padding: '2px 0', + }), + badge: cssObj({ + padding: '2px $1', + backgroundColor: '$gray3', + borderRadius: '$md', + }), + name: cssObj({ + fontWeight: '$semibold', + color: '$gray12', + }), + address: cssObj({ + fontWeight: '$medium', + color: '$gray11', + }), + blue: cssObj({ + fontSize: '$sm', + display: 'flex', + alignItems: 'center', + gap: '$1', + color: '$indigo11', + lineHeight: 'normal', + }), + amountContainer: { + fontWeight: '$semibold', + color: '$gray12', + fontSize: '$sm', + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + }, + assetNft: { + padding: '$1 $2', + }, + asset: { + alignItems: 'center', + gap: '$2', + marginTop: '$1', + }, +}; From 8075534838c39f33c0000567e4b52181fed2011d Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Fri, 31 Jan 2025 11:22:54 -0300 Subject: [PATCH 58/85] feat: enhance transaction operation grouping with child operations --- .../TxOperationsSimple/operations/TxOperation.tsx | 5 ++--- packages/app/src/systems/Transaction/types.tsx | 1 + .../systems/Transaction/utils/simplifyTransaction.ts | 12 ++++++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx index a1d0953fcd..2800f87683 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx @@ -109,12 +109,11 @@ export function TxOperation({ )} {isExpanded && ( - {/* Show individual operations */} - {metadata.groupedAssets?.map((op, idx) => ( + {metadata.childOperations?.map((op, idx) => ( ))} diff --git a/packages/app/src/systems/Transaction/types.tsx b/packages/app/src/systems/Transaction/types.tsx index 5064e99660..337a7b1694 100644 --- a/packages/app/src/systems/Transaction/types.tsx +++ b/packages/app/src/systems/Transaction/types.tsx @@ -72,6 +72,7 @@ export type ContractCallMetadata = { assetAmount?: AssetFuelAmount; operationCount?: number; groupedAssets?: GroupedAssets; + childOperations?: SimplifiedOperation[]; }; export type SwapMetadata = { diff --git a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts index 497094d155..00b9881206 100644 --- a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts +++ b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts @@ -166,15 +166,17 @@ function groupSimilarOperations( const groupedOps = new Map(); for (const op of operations) { + // Group by depth, name, from, and to const key = [ - op.type, + op.metadata.depth, + op.metadata.functionName, op.from.address, op.to.address, - op.metadata.functionName || '', ].join('|'); const existing = groupedOps.get(key); if (!existing) { + // First operation of this type const newOp = { ...op, metadata: { @@ -190,12 +192,14 @@ function groupSimilarOperations( }, } : undefined, + childOperations: [op], // Store individual operations }, }; groupedOps.set(key, newOp); continue; } + // Add to existing group const groupedAssets = existing.metadata.groupedAssets || {}; if (op.amount && op.assetId) { const existingAsset = groupedAssets[op.assetId]; @@ -209,6 +213,10 @@ function groupSimilarOperations( existing.metadata.operationCount = (existing.metadata.operationCount || 1) + 1; existing.metadata.groupedAssets = groupedAssets; + existing.metadata.childOperations = [ + ...(existing.metadata.childOperations || []), + op, + ]; } return Array.from(groupedOps.values()); From 15a806126eeaf98a11d8b0dd68ba13d647988355 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Fri, 31 Jan 2025 11:41:38 -0300 Subject: [PATCH 59/85] feat: add animations to transaction operation expansion --- .../TxOperationsSimple/operations/TxOperation.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx index 2800f87683..a587531d47 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx @@ -3,6 +3,7 @@ import { Box, Icon, Text } from '@fuel-ui/react'; import type { AssetFuelAmount } from '@fuel-wallet/types'; import { useEffect, useState } from 'react'; import { AssetsCache } from '~/systems/Asset/cache/AssetsCache'; +import { MotionBox, animations } from '~/systems/Core'; import { NetworkService } from '~/systems/Network/services/network'; import { TxCategory } from '../../../../types'; import type { SimplifiedOperation } from '../../../../types'; @@ -89,7 +90,8 @@ export function TxOperation({ {metadata.operationCount && metadata.operationCount > 1 && ( - setIsExpanded(!isExpanded)} > @@ -105,18 +107,19 @@ export function TxOperation({ (+{metadata.operationCount} operations) )} - + )} {isExpanded && ( - + {metadata.childOperations?.map((op, idx) => ( ))} - + )} ); From 1638a42014f43f411e4597a201a3ff55e6094fef Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Fri, 31 Jan 2025 11:44:22 -0300 Subject: [PATCH 60/85] feat: add depth-based indentation to transaction operation card --- .../operations/TxOperationCard.tsx | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationCard.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationCard.tsx index 29d6dfaf4c..fec109f659 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationCard.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationCard.tsx @@ -18,11 +18,13 @@ import { TxCategory } from '../../../../types'; type TxOperationCardProps = { operation: SimplifiedOperation; assetsAmount: AssetFuelAmount[]; + depth: number; }; export function TxOperationCard({ operation, assetsAmount, + depth, }: TxOperationCardProps) { const { accounts } = useAccounts(); const isContract = operation.type === TxCategory.CONTRACTCALL; @@ -119,7 +121,7 @@ export function TxOperationCard({ }; return ( - + - {/* From Address */} - + {accountFrom?.name || 'Unknown'} @@ -165,7 +166,6 @@ export function TxOperationCard({ /> - {/* Spacer and Arrow */} @@ -177,7 +177,6 @@ export function TxOperationCard({ {getOperationType()} - {/* Asset Amount */} @@ -187,7 +186,6 @@ export function TxOperationCard({ renderAssets(assetsAmount)} - {/* To Address */} - + {accountTo?.name || 'Unknown'} @@ -265,20 +263,20 @@ const styles = { color: '$indigo11', lineHeight: 'normal', }), - amountContainer: { + amountContainer: cssObj({ fontWeight: '$semibold', color: '$gray12', fontSize: '$sm', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', - }, - assetNft: { + }), + assetNft: cssObj({ padding: '$1 $2', - }, - asset: { + }), + asset: cssObj({ alignItems: 'center', gap: '$2', marginTop: '$1', - }, + }), }; From ed4cfcf1785a41a56ae52820376d20a4e4a9599c Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 09:45:08 -0300 Subject: [PATCH 61/85] style: simplify transaction warning text --- .../Transaction/components/TxViewSimple/TxHeaderSimple.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx index 8d8e0846dc..8169d0883c 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx @@ -8,7 +8,7 @@ export function TxHeaderSimple() { {/* Review Transaction */} - Double-check the details of your transaction before submitting. + Double-check your transaction before submitting. ); From 0b54eac94d3f82613b125914c7f60983573474b6 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 09:45:30 -0300 Subject: [PATCH 62/85] refactor: simplify TxOperationsList component layout and styling --- .../TxViewSimple/TxOperationsSimple/TxOperationsList.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx index 34f459292c..52f2f29c0d 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx @@ -12,17 +12,15 @@ export function TxOperationsList({ operations }: TxOperationsListProps) { operations; return ( - + <> {/* Main operations (transfers and root contract calls related to current account) */} {mainOperations.map((operation, index) => ( @@ -39,13 +37,14 @@ export function TxOperationsList({ operations }: TxOperationsListProps) { /> {/* Intermediate operations with nesting */} + {/* Intermediate opreations exist in the current setup? Wont they all be under the nesting of a root level operation? */} - + ); } From e1a402d4850606026075787480b9c951bdb501ab Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 09:46:22 -0300 Subject: [PATCH 63/85] style: update TxOperationCard styling with refined shadow and layout --- .../TxOperationsSimple/operations/TxOperationCard.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationCard.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationCard.tsx index fec109f659..41ed969991 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationCard.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationCard.tsx @@ -225,12 +225,10 @@ const styles = { contentCol: cssObj({ display: 'flex', backgroundColor: '$gray1', - boxShadow: '0px 2px 6px -1px $colors$gray4, 0px 0px 0px 1px $colors$gray6', + boxShadow: '0px 2px 6px -1px #2020201A, 0px 0px 0px 1px #2020201F', flex: 1, borderRadius: '8px', - minWidth: 0, padding: '14px 12px', - margin: '0 4px', }), spacer: cssObj({ minHeight: '14px', @@ -260,7 +258,7 @@ const styles = { display: 'flex', alignItems: 'center', gap: '$1', - color: '$indigo11', + color: '$indigo10', lineHeight: 'normal', }), amountContainer: cssObj({ From d9b0b00eeaae067b0eea5aa3e1a12b0213f3ab1d Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 09:46:34 -0300 Subject: [PATCH 64/85] style: refine TxOperationCard visual styling and shadows --- .../app/src/systems/Transaction/utils/simplifyTransaction.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts index 00b9881206..2d1812e374 100644 --- a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts +++ b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts @@ -257,6 +257,11 @@ function categorizeOperations( intermediate.push(op); } + // set all main operations to depth 0 + for (const op of main) { + op.metadata.depth = 0; + } + // Group similar operations in each category return { mainOperations: groupSimilarOperations(main), From 11fa6ffc4e159b8469b2a2cbe50171d41b05c596 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 09:47:29 -0300 Subject: [PATCH 65/85] feat: pass depth to TxOperationCard and refine nested operations layout --- .../operations/TxOperation.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx index a587531d47..97c952861f 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx @@ -71,15 +71,13 @@ const fetchAssetsAmount = async (operation: SimplifiedOperation) => { export function TxOperation({ operation, - showNesting = true, + // showNesting = true, }: TxOperationProps) { const metadata = operation.metadata; - const isContract = operation.type === TxCategory.CONTRACTCALL; + // const isContract = operation.type === TxCategory.CONTRACTCALL; const depth = metadata?.depth || 0; - console.log(showNesting, isContract, depth); const [assetsAmount, setAssetsAmount] = useState([]); const [isExpanded, setIsExpanded] = useState(false); - console.log(operation); useEffect(() => { fetchAssetsAmount(operation).then(setAssetsAmount); }, [operation]); @@ -88,7 +86,11 @@ export function TxOperation({ return ( - + {metadata.operationCount && metadata.operationCount > 1 && ( Date: Mon, 3 Feb 2025 11:14:52 -0300 Subject: [PATCH 66/85] base: use tx simple in transactions list --- .../operations/TxOperation.tsx | 2 +- .../components/TxViewSimple/TxViewSimple.tsx | 1 + .../components/TxViewSimpleWrapper.tsx | 8 +++++-- .../Transaction/pages/TxView/TxView.tsx | 21 ++++++++++--------- .../Transaction/utils/simplifyTransaction.ts | 9 ++++++-- 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx index 97c952861f..addf0b376c 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx @@ -85,7 +85,7 @@ export function TxOperation({ // if (isContract && !showNesting && depth !== 0) return null; return ( - + + {/* TODO: The header should not show in transaction history */} diff --git a/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx b/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx index a71fc8687d..5fc5fe3100 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx @@ -1,9 +1,13 @@ -import type { TransactionRequest, TransactionSummary } from 'fuels'; +import type { + TransactionRequest, + TransactionResult, + TransactionSummary, +} from 'fuels'; import { useSimplifiedTransaction } from '../hooks/useSimplifiedTransaction'; import { TxViewSimple } from './TxViewSimple'; type TxViewSimpleWrapperProps = { - summary?: TransactionSummary; + summary?: TransactionSummary | TransactionResult; request?: TransactionRequest; showDetails?: boolean; isLoading?: boolean; diff --git a/packages/app/src/systems/Transaction/pages/TxView/TxView.tsx b/packages/app/src/systems/Transaction/pages/TxView/TxView.tsx index 3a8767d0b6..5efe934f8a 100644 --- a/packages/app/src/systems/Transaction/pages/TxView/TxView.tsx +++ b/packages/app/src/systems/Transaction/pages/TxView/TxView.tsx @@ -3,9 +3,14 @@ import { useNavigate, useParams } from 'react-router-dom'; import { Layout } from '~/systems/Core'; import { useNetworks } from '~/systems/Network'; import type { SendFormValues } from '~/systems/Send/hooks'; -import { TxStatusAlert } from '../../components'; +import { + TxStatusAlert, + TxViewSimple, + TxViewSimpleWrapper, +} from '../../components'; import { TxContent } from '../../components/TxContent'; import { useTxResult } from '../../hooks'; +import { simplifyTransaction } from '../../utils/simplifyTransaction'; export function TxView() { const txIdQueryParam = useParams<{ txId: string }>().txId; @@ -17,7 +22,6 @@ export function TxView() { txId: txIdQueryParam, waitProviderUrl: true, }); - const form = useForm(); return ( navigate(-1)} /> - {!txResult && } {ctx.shouldShowAlert && ( )} {txResult && ( - - - + )} diff --git a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts index 2d1812e374..7715348fcb 100644 --- a/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts +++ b/packages/app/src/systems/Transaction/utils/simplifyTransaction.ts @@ -1,4 +1,9 @@ -import type { Operation, TransactionRequest, TransactionSummary } from 'fuels'; +import type { + Operation, + TransactionRequest, + TransactionResult, + TransactionSummary, +} from 'fuels'; import { BN, type OperationFunctionCall, @@ -271,7 +276,7 @@ function categorizeOperations( } export function simplifyTransaction( - summary: TransactionSummary, + summary: TransactionSummary | TransactionResult, request?: TransactionRequest, currentAccount?: string ): SimplifiedTransaction { From fd0aa44eaeb8fccdc97ebdb37a31c75c28e6c635 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 12:06:37 -0300 Subject: [PATCH 67/85] feat: add variant support for transaction view components --- .../src/systems/Core/components/Layout/Layout.tsx | 8 ++++---- .../components/TxViewSimple/TxViewSimple.tsx | 15 +++++++++++---- .../components/TxViewSimpleWrapper.tsx | 5 ++++- .../systems/Transaction/pages/TxView/TxView.tsx | 11 ++--------- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/packages/app/src/systems/Core/components/Layout/Layout.tsx b/packages/app/src/systems/Core/components/Layout/Layout.tsx index 1f95d9f722..6cf43a72db 100644 --- a/packages/app/src/systems/Core/components/Layout/Layout.tsx +++ b/packages/app/src/systems/Core/components/Layout/Layout.tsx @@ -128,7 +128,7 @@ export const styles = { ...coreStyles.scrollable(), '&:has(.layout__bottom) .layout__content': { - pb: '$0', + padding: '$0', }, }), wrapper: cssObj({ @@ -149,9 +149,9 @@ export const styles = { padding: '$0 $4 $4 $4', flex: 1, '&[data-scrollable=true]:not([data-noborder])': { - padding: '$0 $0 $4 $4', - ...coreStyles.scrollable(), - overflowY: 'scroll !important', + padding: '$0', + // ...coreStyles.scrollable(), + overflow: 'auto', }, '&[data-noborder]': { padding: '$0', diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx index 4ae91bed8b..86992be762 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx @@ -9,23 +9,30 @@ import { TxFeeSimple } from './TxFeeSimple'; import { TxHeaderSimple } from './TxHeaderSimple'; import { TxOperationsList } from './TxOperationsSimple/TxOperationsList'; +export type TxViewVariant = 'default' | 'history'; + +type TxViewSimpleProps = SimplifiedTransactionViewProps & { + variant?: TxViewVariant; +}; + export function TxViewSimple({ transaction, showDetails = true, isLoading, footer, -}: SimplifiedTransactionViewProps) { + variant = 'default', +}: TxViewSimpleProps) { const [isCustomFees, setIsCustomFees] = useState(false); const [_selectedTip, setSelectedTip] = useState(); + const isHistory = variant === 'history'; // const { account } = useAccounts(); return ( - {/* TODO: The header should not show in transaction history */} - + {!isHistory && } - {showDetails && ( + {showDetails && !isHistory && ( diff --git a/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx b/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx index 5fc5fe3100..aa532477fe 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx @@ -4,7 +4,7 @@ import type { TransactionSummary, } from 'fuels'; import { useSimplifiedTransaction } from '../hooks/useSimplifiedTransaction'; -import { TxViewSimple } from './TxViewSimple'; +import { TxViewSimple, type TxViewVariant } from './TxViewSimple'; type TxViewSimpleWrapperProps = { summary?: TransactionSummary | TransactionResult; @@ -12,6 +12,7 @@ type TxViewSimpleWrapperProps = { showDetails?: boolean; isLoading?: boolean; footer?: React.ReactNode; + variant?: TxViewVariant; }; export function TxViewSimpleWrapper({ @@ -20,6 +21,7 @@ export function TxViewSimpleWrapper({ showDetails, isLoading: externalLoading, footer, + variant, }: TxViewSimpleWrapperProps) { // If we have a summary but no explicit status, treat it as pending const hasValidStatus = !!summary; @@ -40,6 +42,7 @@ export function TxViewSimpleWrapper({ transaction={transaction} showDetails={showDetails} footer={footer} + variant={variant} /> ); } diff --git a/packages/app/src/systems/Transaction/pages/TxView/TxView.tsx b/packages/app/src/systems/Transaction/pages/TxView/TxView.tsx index 5efe934f8a..ff3afdae80 100644 --- a/packages/app/src/systems/Transaction/pages/TxView/TxView.tsx +++ b/packages/app/src/systems/Transaction/pages/TxView/TxView.tsx @@ -1,16 +1,8 @@ -import { FormProvider, useForm } from 'react-hook-form'; import { useNavigate, useParams } from 'react-router-dom'; import { Layout } from '~/systems/Core'; import { useNetworks } from '~/systems/Network'; -import type { SendFormValues } from '~/systems/Send/hooks'; -import { - TxStatusAlert, - TxViewSimple, - TxViewSimpleWrapper, -} from '../../components'; -import { TxContent } from '../../components/TxContent'; +import { TxStatusAlert, TxViewSimpleWrapper } from '../../components'; import { useTxResult } from '../../hooks'; -import { simplifyTransaction } from '../../utils/simplifyTransaction'; export function TxView() { const txIdQueryParam = useParams<{ txId: string }>().txId; @@ -38,6 +30,7 @@ export function TxView() { summary={txResult} showDetails={ctx.shouldShowTxFee} isLoading={!txResult} + variant="history" /> )}
From 5aec855fdafc12c1047df08b2f202c8ba9640a26 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 13:55:26 -0300 Subject: [PATCH 68/85] refactor: update TxApprove page to use TxViewSimpleWrapper --- .../Transaction/pages/TxApprove/TxApprove.tsx | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx b/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx index 5c1b9104fc..8f7688b0cd 100644 --- a/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx +++ b/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx @@ -6,7 +6,7 @@ import { Pages } from '~/systems/Core'; import { coreStyles } from '~/systems/Core/styles'; import { useTransactionRequest } from '~/systems/DApp'; import { OverlayDialogTopbar } from '~/systems/Overlay'; -import { TxContent } from '~/systems/Transaction'; +import { TxViewSimpleWrapper } from '../../components'; export const TxApprove = () => { const ctx = useTransactionRequest(); @@ -29,21 +29,32 @@ export const TxApprove = () => { {ctx.title} - {!ctx.txSummarySimulated && } {ctx.shouldShowTxSimulated && ( - + Try again + + ) + } /> )} {ctx.shouldShowTxExecuted && ( - Date: Mon, 3 Feb 2025 15:30:54 -0300 Subject: [PATCH 69/85] diff: clean --- .../systems/Core/components/Layout/Layout.tsx | 5 ++--- .../systems/Ecosystem/services/ecosystem.ts | 18 ++++-------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/packages/app/src/systems/Core/components/Layout/Layout.tsx b/packages/app/src/systems/Core/components/Layout/Layout.tsx index 6cf43a72db..efb7bbe7c4 100644 --- a/packages/app/src/systems/Core/components/Layout/Layout.tsx +++ b/packages/app/src/systems/Core/components/Layout/Layout.tsx @@ -128,7 +128,7 @@ export const styles = { ...coreStyles.scrollable(), '&:has(.layout__bottom) .layout__content': { - padding: '$0', + pb: '$0', }, }), wrapper: cssObj({ @@ -150,8 +150,7 @@ export const styles = { flex: 1, '&[data-scrollable=true]:not([data-noborder])': { padding: '$0', - // ...coreStyles.scrollable(), - overflow: 'auto', + ...coreStyles.scrollable(), }, '&[data-noborder]': { padding: '$0', diff --git a/packages/app/src/systems/Ecosystem/services/ecosystem.ts b/packages/app/src/systems/Ecosystem/services/ecosystem.ts index d6a2aa767f..df384a9d5d 100644 --- a/packages/app/src/systems/Ecosystem/services/ecosystem.ts +++ b/packages/app/src/systems/Ecosystem/services/ecosystem.ts @@ -4,23 +4,13 @@ import { ECOSYSTEM_PROJECTS_URL } from '~/config'; // biome-ignore lint/complexity/noStaticOnlyClass: export class EcosystemService { static async fetchProjects() { - try { - const res = await fetch(ECOSYSTEM_PROJECTS_URL); - - if (!res.ok) { - console.error('Failed to fetch projects:', res.status, res.statusText); - const text = await res.text(); - console.error('Response text:', text); - throw new Error( - `Failed to fetch projects: ${res.status} ${res.statusText}` - ); - } + const res = await fetch(ECOSYSTEM_PROJECTS_URL); + if (res.ok) { const data: EcosystemProject[] = await res.json(); return data; - } catch (err) { - console.error('Error fetching projects:', err); - throw err; } + + return []; } } From 303520651f7fb59107be706086cb22398ad5df2c Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 15:34:52 -0300 Subject: [PATCH 70/85] chore: remove redundant wrapper --- .../components/TxViewSimple/TxViewSimple.tsx | 37 +++++++++++++++---- .../Transaction/pages/TxView/TxView.tsx | 4 +- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx index 86992be762..cadb99391e 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxViewSimple/TxViewSimple.tsx @@ -1,9 +1,13 @@ import { cssObj } from '@fuel-ui/css'; import { Box, Icon, Text } from '@fuel-ui/react'; -import type { BN } from 'fuels'; +import type { + BN, + TransactionRequest, + TransactionResult, + TransactionSummary, +} from 'fuels'; import { useState } from 'react'; -import { useAccounts } from '~/systems/Account'; -import type { SimplifiedTransactionViewProps } from '../../types'; +import { useSimplifiedTransaction } from '../../hooks/useSimplifiedTransaction'; import { TxFeeOptionsSimple } from './TxFeeOptionsSimple'; import { TxFeeSimple } from './TxFeeSimple'; import { TxHeaderSimple } from './TxHeaderSimple'; @@ -11,21 +15,38 @@ import { TxOperationsList } from './TxOperationsSimple/TxOperationsList'; export type TxViewVariant = 'default' | 'history'; -type TxViewSimpleProps = SimplifiedTransactionViewProps & { +type TxViewSimpleProps = { + summary?: TransactionSummary | TransactionResult; + request?: TransactionRequest; + showDetails?: boolean; + isLoading?: boolean; + footer?: React.ReactNode; variant?: TxViewVariant; }; export function TxViewSimple({ - transaction, + summary, + request, showDetails = true, - isLoading, + isLoading: externalLoading, footer, variant = 'default', }: TxViewSimpleProps) { const [isCustomFees, setIsCustomFees] = useState(false); const [_selectedTip, setSelectedTip] = useState(); const isHistory = variant === 'history'; - // const { account } = useAccounts(); + + const hasValidStatus = !!summary; + if (!hasValidStatus || externalLoading) { + return ; + } + + const { transaction, isReady } = useSimplifiedTransaction({ + summary, + request, + }); + + if (!isReady || !transaction) return null; return ( @@ -47,7 +68,7 @@ export function TxViewSimple({ ) : ( setIsCustomFees(true)} onFeeSelect={setSelectedTip} /> diff --git a/packages/app/src/systems/Transaction/pages/TxView/TxView.tsx b/packages/app/src/systems/Transaction/pages/TxView/TxView.tsx index ff3afdae80..2e000d0947 100644 --- a/packages/app/src/systems/Transaction/pages/TxView/TxView.tsx +++ b/packages/app/src/systems/Transaction/pages/TxView/TxView.tsx @@ -1,7 +1,7 @@ import { useNavigate, useParams } from 'react-router-dom'; import { Layout } from '~/systems/Core'; import { useNetworks } from '~/systems/Network'; -import { TxStatusAlert, TxViewSimpleWrapper } from '../../components'; +import { TxStatusAlert, TxViewSimple } from '../../components'; import { useTxResult } from '../../hooks'; export function TxView() { @@ -26,7 +26,7 @@ export function TxView() { )} {txResult && ( - Date: Mon, 3 Feb 2025 16:51:17 -0300 Subject: [PATCH 71/85] rename TxViewSimple --- .../TransactionRequest/TransactionRequest.tsx | 9 ++-- .../TxDetails.tsx} | 25 ++-------- .../TxFeeOptionsSimple.tsx | 0 .../TxFeeSimple.tsx | 0 .../TxHeaderSimple.tsx | 0 .../TxOperationsSimple/TxOperationDrawer.tsx | 0 .../TxOperationsSimple/TxOperationHeader.tsx | 0 .../TxOperationsSimple/TxOperationsGroup.tsx | 0 .../TxOperationsSimple/TxOperationsList.tsx | 0 .../TxOperationsSimple/index.ts | 0 .../operations/TxOperation.tsx | 0 .../operations/TxOperationCard.tsx | 0 .../operations/TxOperationNesting.tsx | 0 .../TxOperationsSimple/useEcosystemProject.ts | 0 .../TxOperationsSimple/utils.ts | 0 .../{TxViewSimple => TxDetails}/index.tsx | 2 +- .../components/TxViewSimpleWrapper.tsx | 48 ------------------- .../systems/Transaction/components/index.tsx | 3 +- .../Transaction/pages/TxApprove/TxApprove.tsx | 6 +-- .../Transaction/pages/TxView/TxView.tsx | 5 +- 20 files changed, 18 insertions(+), 80 deletions(-) rename packages/app/src/systems/Transaction/components/{TxViewSimple/TxViewSimple.tsx => TxDetails/TxDetails.tsx} (84%) rename packages/app/src/systems/Transaction/components/{TxViewSimple => TxDetails}/TxFeeOptionsSimple.tsx (100%) rename packages/app/src/systems/Transaction/components/{TxViewSimple => TxDetails}/TxFeeSimple.tsx (100%) rename packages/app/src/systems/Transaction/components/{TxViewSimple => TxDetails}/TxHeaderSimple.tsx (100%) rename packages/app/src/systems/Transaction/components/{TxViewSimple => TxDetails}/TxOperationsSimple/TxOperationDrawer.tsx (100%) rename packages/app/src/systems/Transaction/components/{TxViewSimple => TxDetails}/TxOperationsSimple/TxOperationHeader.tsx (100%) rename packages/app/src/systems/Transaction/components/{TxViewSimple => TxDetails}/TxOperationsSimple/TxOperationsGroup.tsx (100%) rename packages/app/src/systems/Transaction/components/{TxViewSimple => TxDetails}/TxOperationsSimple/TxOperationsList.tsx (100%) rename packages/app/src/systems/Transaction/components/{TxViewSimple => TxDetails}/TxOperationsSimple/index.ts (100%) rename packages/app/src/systems/Transaction/components/{TxViewSimple => TxDetails}/TxOperationsSimple/operations/TxOperation.tsx (100%) rename packages/app/src/systems/Transaction/components/{TxViewSimple => TxDetails}/TxOperationsSimple/operations/TxOperationCard.tsx (100%) rename packages/app/src/systems/Transaction/components/{TxViewSimple => TxDetails}/TxOperationsSimple/operations/TxOperationNesting.tsx (100%) rename packages/app/src/systems/Transaction/components/{TxViewSimple => TxDetails}/TxOperationsSimple/useEcosystemProject.ts (100%) rename packages/app/src/systems/Transaction/components/{TxViewSimple => TxDetails}/TxOperationsSimple/utils.ts (100%) rename packages/app/src/systems/Transaction/components/{TxViewSimple => TxDetails}/index.tsx (76%) delete mode 100644 packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx diff --git a/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx b/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx index f43023104d..42e8125678 100644 --- a/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx +++ b/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx @@ -6,7 +6,8 @@ import { useAssets } from '~/systems/Asset'; import { Layout } from '~/systems/Core'; import { TopBarType } from '~/systems/Core/components/Layout/TopBar'; import { - TxViewSimpleWrapper, + TxContent, + TxDetails, getGasLimitFromTxRequest, } from '~/systems/Transaction'; import { formatTip } from '~/systems/Transaction/components/TxFeeOptions/TxFeeOptions.utils'; @@ -64,7 +65,7 @@ export function TransactionRequest() { - + ); @@ -86,7 +87,7 @@ export function TransactionRequest() { {shouldShowTxSimulated && ( - )} {shouldShowTxExecuted && ( - (); + const [_, setSelectedTip] = useState(); const isHistory = variant === 'history'; - const hasValidStatus = !!summary; - if (!hasValidStatus || externalLoading) { - return ; - } - const { transaction, isReady } = useSimplifiedTransaction({ summary, request, @@ -114,14 +110,3 @@ const styles = { }, }), }; - -// Add a loader component for loading states -TxViewSimple.Loader = function TxViewSimpleLoader() { - return ( - - - - - - ); -}; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxFeeOptionsSimple.tsx similarity index 100% rename from packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeOptionsSimple.tsx rename to packages/app/src/systems/Transaction/components/TxDetails/TxFeeOptionsSimple.tsx diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxFeeSimple.tsx similarity index 100% rename from packages/app/src/systems/Transaction/components/TxViewSimple/TxFeeSimple.tsx rename to packages/app/src/systems/Transaction/components/TxDetails/TxFeeSimple.tsx diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxHeaderSimple.tsx similarity index 100% rename from packages/app/src/systems/Transaction/components/TxViewSimple/TxHeaderSimple.tsx rename to packages/app/src/systems/Transaction/components/TxDetails/TxHeaderSimple.tsx diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationDrawer.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationDrawer.tsx similarity index 100% rename from packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationDrawer.tsx rename to packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationDrawer.tsx diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationHeader.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationHeader.tsx similarity index 100% rename from packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationHeader.tsx rename to packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationHeader.tsx diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationsGroup.tsx similarity index 100% rename from packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsGroup.tsx rename to packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationsGroup.tsx diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationsList.tsx similarity index 100% rename from packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/TxOperationsList.tsx rename to packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationsList.tsx diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/index.ts b/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/index.ts similarity index 100% rename from packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/index.ts rename to packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/index.ts diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/operations/TxOperation.tsx similarity index 100% rename from packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperation.tsx rename to packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/operations/TxOperation.tsx diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationCard.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/operations/TxOperationCard.tsx similarity index 100% rename from packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationCard.tsx rename to packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/operations/TxOperationCard.tsx diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationNesting.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/operations/TxOperationNesting.tsx similarity index 100% rename from packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/operations/TxOperationNesting.tsx rename to packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/operations/TxOperationNesting.tsx diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/useEcosystemProject.ts b/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/useEcosystemProject.ts similarity index 100% rename from packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/useEcosystemProject.ts rename to packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/useEcosystemProject.ts diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/utils.ts b/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/utils.ts similarity index 100% rename from packages/app/src/systems/Transaction/components/TxViewSimple/TxOperationsSimple/utils.ts rename to packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/utils.ts diff --git a/packages/app/src/systems/Transaction/components/TxViewSimple/index.tsx b/packages/app/src/systems/Transaction/components/TxDetails/index.tsx similarity index 76% rename from packages/app/src/systems/Transaction/components/TxViewSimple/index.tsx rename to packages/app/src/systems/Transaction/components/TxDetails/index.tsx index 8d1cc03cf5..24f1e90788 100644 --- a/packages/app/src/systems/Transaction/components/TxViewSimple/index.tsx +++ b/packages/app/src/systems/Transaction/components/TxDetails/index.tsx @@ -1,4 +1,4 @@ export * from './TxOperationsSimple'; -export * from './TxViewSimple'; export * from './TxHeaderSimple'; export * from './TxFeeSimple'; +export * from './TxDetails'; diff --git a/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx b/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx deleted file mode 100644 index aa532477fe..0000000000 --- a/packages/app/src/systems/Transaction/components/TxViewSimpleWrapper.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import type { - TransactionRequest, - TransactionResult, - TransactionSummary, -} from 'fuels'; -import { useSimplifiedTransaction } from '../hooks/useSimplifiedTransaction'; -import { TxViewSimple, type TxViewVariant } from './TxViewSimple'; - -type TxViewSimpleWrapperProps = { - summary?: TransactionSummary | TransactionResult; - request?: TransactionRequest; - showDetails?: boolean; - isLoading?: boolean; - footer?: React.ReactNode; - variant?: TxViewVariant; -}; - -export function TxViewSimpleWrapper({ - summary, - request, - showDetails, - isLoading: externalLoading, - footer, - variant, -}: TxViewSimpleWrapperProps) { - // If we have a summary but no explicit status, treat it as pending - const hasValidStatus = !!summary; - - if (!hasValidStatus || externalLoading) { - return ; - } - - const { transaction, isReady } = useSimplifiedTransaction({ - summary, - request, - }); - - if (!isReady || !transaction) return null; - - return ( - - ); -} diff --git a/packages/app/src/systems/Transaction/components/index.tsx b/packages/app/src/systems/Transaction/components/index.tsx index 8ef934ecd2..414977cb82 100644 --- a/packages/app/src/systems/Transaction/components/index.tsx +++ b/packages/app/src/systems/Transaction/components/index.tsx @@ -7,5 +7,4 @@ export * from './TxOperation'; export * from './TxOperations'; export * from './TxRecipientCard'; export * from './TxStatusAlert'; -export * from './TxViewSimple'; -export * from './TxViewSimpleWrapper'; +export * from './TxDetails'; diff --git a/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx b/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx index 8f7688b0cd..dd64b3281e 100644 --- a/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx +++ b/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx @@ -6,7 +6,7 @@ import { Pages } from '~/systems/Core'; import { coreStyles } from '~/systems/Core/styles'; import { useTransactionRequest } from '~/systems/DApp'; import { OverlayDialogTopbar } from '~/systems/Overlay'; -import { TxViewSimpleWrapper } from '../../components'; +import { TxDetails } from '../../components/TxDetails/TxDetails'; export const TxApprove = () => { const ctx = useTransactionRequest(); @@ -30,7 +30,7 @@ export const TxApprove = () => { {ctx.shouldShowTxSimulated && ( - { /> )} {ctx.shouldShowTxExecuted && ( - )} {txResult && ( - Date: Mon, 3 Feb 2025 16:54:05 -0300 Subject: [PATCH 72/85] feat: add TxFeeOptions component for custom transaction fees --- .../systems/Transaction/components/TxDetails/TxDetails.tsx | 5 ++--- .../TxDetails/{TxFeeOptionsSimple.tsx => TxFeeOptions.tsx} | 6 +++--- .../src/systems/Transaction/components/TxDetails/index.tsx | 1 + 3 files changed, 6 insertions(+), 6 deletions(-) rename packages/app/src/systems/Transaction/components/TxDetails/{TxFeeOptionsSimple.tsx => TxFeeOptions.tsx} (96%) diff --git a/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx index fbb6793d9e..0d99a264c0 100644 --- a/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx +++ b/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx @@ -8,9 +8,8 @@ import type { TransactionSummary, } from 'fuels'; import { useState } from 'react'; +import { TxFeeOptions, TxFeeSimple } from '.'; import { useSimplifiedTransaction } from '../../hooks/useSimplifiedTransaction'; -import { TxFeeOptionsSimple } from './TxFeeOptionsSimple'; -import { TxFeeSimple } from './TxFeeSimple'; import { TxHeaderSimple } from './TxHeaderSimple'; import { TxOperationsList } from './TxOperationsSimple/TxOperationsList'; @@ -56,7 +55,7 @@ export function TxDetails({ Fee (network) {isCustomFees ? ( - setIsCustomFees(false)} onTipChange={setSelectedTip} diff --git a/packages/app/src/systems/Transaction/components/TxDetails/TxFeeOptionsSimple.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxFeeOptions.tsx similarity index 96% rename from packages/app/src/systems/Transaction/components/TxDetails/TxFeeOptionsSimple.tsx rename to packages/app/src/systems/Transaction/components/TxDetails/TxFeeOptions.tsx index 46802db6f7..ab405302e7 100644 --- a/packages/app/src/systems/Transaction/components/TxDetails/TxFeeOptionsSimple.tsx +++ b/packages/app/src/systems/Transaction/components/TxDetails/TxFeeOptions.tsx @@ -6,7 +6,7 @@ import { useState } from 'react'; import { createAmount } from '~/systems/Core/components/InputAmount/InputAmount'; import { isAmountAllowed } from '~/systems/Core/components/InputAmount/InputAmount.utils'; -type TxFeeOptionsSimpleProps = { +type TxFeeOptionsProps = { baseFee: BN; onBack: () => void; onTipChange?: (tip: BN) => void; @@ -14,11 +14,11 @@ type TxFeeOptionsSimpleProps = { const DECIMAL_UNITS = 9; -export function TxFeeOptionsSimple({ +export function TxFeeOptions({ baseFee, onBack, onTipChange, -}: TxFeeOptionsSimpleProps) { +}: TxFeeOptionsProps) { const [customTip, setCustomTip] = useState('0'); const handleTipChange = (text: string) => { diff --git a/packages/app/src/systems/Transaction/components/TxDetails/index.tsx b/packages/app/src/systems/Transaction/components/TxDetails/index.tsx index 24f1e90788..5d89f9e5f8 100644 --- a/packages/app/src/systems/Transaction/components/TxDetails/index.tsx +++ b/packages/app/src/systems/Transaction/components/TxDetails/index.tsx @@ -2,3 +2,4 @@ export * from './TxOperationsSimple'; export * from './TxHeaderSimple'; export * from './TxFeeSimple'; export * from './TxDetails'; +export * from './TxFeeOptions'; From cfadb5cc1842ce6ff3d2942014235ac906ff5a88 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 16:59:12 -0300 Subject: [PATCH 73/85] merge: txfee --- .../components/TxDetails/TxDetails.tsx | 5 +- .../components/TxDetails/TxFeeSimple.tsx | 170 -------------- .../components/TxDetails/index.tsx | 1 - .../components/TxFee/TxFee.stories.tsx | 24 -- .../components/TxFee/TxFee.test.tsx | 19 -- .../Transaction/components/TxFee/TxFee.tsx | 207 ++++++++++++++---- .../components/TxFee/TxFeeLoader.tsx | 15 -- .../Transaction/components/TxFee/styles.tsx | 40 ---- .../components/TxFeeOptions/TxFeeOptions.tsx | 6 - 9 files changed, 165 insertions(+), 322 deletions(-) delete mode 100644 packages/app/src/systems/Transaction/components/TxDetails/TxFeeSimple.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxFee/TxFee.stories.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxFee/TxFee.test.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxFee/TxFeeLoader.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxFee/styles.tsx diff --git a/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx index 0d99a264c0..24ab0a3887 100644 --- a/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx +++ b/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx @@ -8,8 +8,9 @@ import type { TransactionSummary, } from 'fuels'; import { useState } from 'react'; -import { TxFeeOptions, TxFeeSimple } from '.'; +import { TxFeeOptions } from '.'; import { useSimplifiedTransaction } from '../../hooks/useSimplifiedTransaction'; +import { TxFee } from '../TxFee'; import { TxHeaderSimple } from './TxHeaderSimple'; import { TxOperationsList } from './TxOperationsSimple/TxOperationsList'; @@ -61,7 +62,7 @@ export function TxDetails({ onTipChange={setSelectedTip} /> ) : ( - setIsCustomFees(true)} diff --git a/packages/app/src/systems/Transaction/components/TxDetails/TxFeeSimple.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxFeeSimple.tsx deleted file mode 100644 index cc966b4f15..0000000000 --- a/packages/app/src/systems/Transaction/components/TxDetails/TxFeeSimple.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { - Box, - Button, - ContentLoader, - RadioGroup, - RadioGroupItem, - Text, -} from '@fuel-ui/react'; -import type { BN } from 'fuels'; -import { DEFAULT_PRECISION } from 'fuels'; -import { useEffect, useRef, useState } from 'react'; -import { TxService } from '../../services'; -import type { SimplifiedFee } from '../../types'; - -type TxFeeSimpleProps = { - fee: SimplifiedFee; - isLoading?: boolean; - onCustomFees?: () => void; - onFeeSelect?: (tip: BN) => void; -}; - -type FeeOption = 'regular' | 'fast'; - -export function TxFeeSimple({ - fee, - isLoading, - onCustomFees, - onFeeSelect, -}: TxFeeSimpleProps) { - const [selectedOption, setSelectedOption] = useState('regular'); - const [tips, setTips] = useState<{ regularTip: BN; fastTip: BN }>(); - const previousDefaultTip = useRef(); - - useEffect(() => { - async function getTips() { - const { regularTip, fastTip } = await TxService.estimateDefaultTips(); - setTips({ regularTip, fastTip }); - // Set initial tip - previousDefaultTip.current = regularTip; - onFeeSelect?.(regularTip); - } - getTips(); - }, [onFeeSelect]); - - const handleOptionSelect = (option: FeeOption) => { - setSelectedOption(option); - if (!tips) return; - - const newTip = option === 'regular' ? tips.regularTip : tips.fastTip; - previousDefaultTip.current = newTip; - onFeeSelect?.(newTip); - }; - - if (isLoading) return ; - - const options = [ - { - id: 'regular', - name: 'Regular', - fee: tips ? fee.network.add(tips.regularTip) : fee.network, - }, - { - id: 'fast', - name: 'Fast', - fee: tips ? fee.network.add(tips.fastTip) : fee.network, - }, - ]; - - return ( - - - - {options.map((option) => ( - handleOptionSelect(option.id as FeeOption)} - > - handleOptionSelect(option.id as FeeOption)} - /> - - - {option.fee - ? `${option.fee.format({ - minPrecision: DEFAULT_PRECISION, - precision: DEFAULT_PRECISION, - })} ETH` - : '--'} - - - ))} - - - - - ); -} - -TxFeeSimple.Loader = function TxFeeSimpleLoader() { - return ( - - - - - - - - - ); -}; - -const styles = { - content: cssObj({ - display: 'flex', - flexDirection: 'column', - gap: '$3', - padding: '$3', - }), - option: cssObj({ - alignItems: 'center', - backgroundColor: '$white', - border: '1px solid #e0e0e0', - borderRadius: '10px', - color: '#646464', - cursor: 'pointer', - fontSize: '13px', - gap: '$3', - justifyContent: 'space-between', - padding: '$3', - transition: 'all 0.2s ease', - boxShadow: '0px 2px 6px -1px #2020201A, 0px 0px 0px 1px #2020201F', - - '&:hover': { - backgroundColor: '#f0f0f0', - }, - }), - optionContent: cssObj({ - color: '#202020', - }), - optionLabel: cssObj({ - color: '#202020', - fontSize: '13px', - fontWeight: '$medium', - }), - radio: cssObj({ - cursor: 'pointer', - height: '16px', - margin: 0, - width: '16px', - }), - customFeesBtn: cssObj({ - alignSelf: 'center', - color: '$accent11', - fontSize: '$sm', - mt: '$2', - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxDetails/index.tsx b/packages/app/src/systems/Transaction/components/TxDetails/index.tsx index 5d89f9e5f8..8d55c16e40 100644 --- a/packages/app/src/systems/Transaction/components/TxDetails/index.tsx +++ b/packages/app/src/systems/Transaction/components/TxDetails/index.tsx @@ -1,5 +1,4 @@ export * from './TxOperationsSimple'; export * from './TxHeaderSimple'; -export * from './TxFeeSimple'; export * from './TxDetails'; export * from './TxFeeOptions'; diff --git a/packages/app/src/systems/Transaction/components/TxFee/TxFee.stories.tsx b/packages/app/src/systems/Transaction/components/TxFee/TxFee.stories.tsx deleted file mode 100644 index c1afa158cb..0000000000 --- a/packages/app/src/systems/Transaction/components/TxFee/TxFee.stories.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Box } from '@fuel-ui/react'; -import { bn } from 'fuels'; - -import type { TxFeeProps } from './TxFee'; -import { TxFee } from './TxFee'; - -export default { - component: TxFee, - title: 'Transaction/Components/TxFee', -}; - -export const Usage = (args: TxFeeProps) => ( - - - -); - -export const Loader = () => ( - - - -); diff --git a/packages/app/src/systems/Transaction/components/TxFee/TxFee.test.tsx b/packages/app/src/systems/Transaction/components/TxFee/TxFee.test.tsx deleted file mode 100644 index 5766a5fc05..0000000000 --- a/packages/app/src/systems/Transaction/components/TxFee/TxFee.test.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { render, screen, testA11y } from '@fuel-ui/test-utils'; -import { bn } from 'fuels'; - -import { TxFee } from './TxFee'; - -describe('TxFee', () => { - it('a11y', async () => { - await testA11y(); - }); - - it('should be able to show the transaction fee', async () => { - const feeCost = bn(6); - render(); - expect(await screen.findByText(/fee \(network\)/i)).toBeInTheDocument(); - const valFee = screen.getByLabelText(/Fee value/i); - expect(valFee).toBeInTheDocument(); - expect(valFee.innerHTML.trim()).toBe(`${feeCost.format()} ETH`); - }); -}); diff --git a/packages/app/src/systems/Transaction/components/TxFee/TxFee.tsx b/packages/app/src/systems/Transaction/components/TxFee/TxFee.tsx index 01bde372be..76a18d18e1 100644 --- a/packages/app/src/systems/Transaction/components/TxFee/TxFee.tsx +++ b/packages/app/src/systems/Transaction/components/TxFee/TxFee.tsx @@ -1,53 +1,170 @@ -import { Card, Text } from '@fuel-ui/react'; -import { type BN, DEFAULT_PRECISION } from 'fuels'; -import type { FC } from 'react'; - -import { TxFeeLoader } from './TxFeeLoader'; -import { styles } from './styles'; - -export type TxFeeProps = { - fee?: BN; - checked?: boolean; - onChecked?: (checked: boolean) => void; - title?: string; -}; +import { cssObj } from '@fuel-ui/css'; +import { + Box, + Button, + ContentLoader, + RadioGroup, + RadioGroupItem, + Text, +} from '@fuel-ui/react'; +import type { BN } from 'fuels'; +import { DEFAULT_PRECISION } from 'fuels'; +import { useEffect, useRef, useState } from 'react'; +import { TxService } from '../../services'; +import type { SimplifiedFee } from '../../types'; -type TxFeeComponent = FC & { - Loader: typeof TxFeeLoader; +type TxFeeProps = { + fee: SimplifiedFee; + isLoading?: boolean; + onCustomFees?: () => void; + onFeeSelect?: (tip: BN) => void; }; -export const TxFee: TxFeeComponent = ({ +type FeeOption = 'regular' | 'fast'; + +export function TxFee({ fee, - checked, - onChecked, - title, -}: TxFeeProps) => { + isLoading, + onCustomFees, + onFeeSelect, +}: TxFeeProps) { + const [selectedOption, setSelectedOption] = useState('regular'); + const [tips, setTips] = useState<{ regularTip: BN; fastTip: BN }>(); + const previousDefaultTip = useRef(); + + useEffect(() => { + async function getTips() { + const { regularTip, fastTip } = await TxService.estimateDefaultTips(); + setTips({ regularTip, fastTip }); + // Set initial tip + previousDefaultTip.current = regularTip; + onFeeSelect?.(regularTip); + } + getTips(); + }, [onFeeSelect]); + + const handleOptionSelect = (option: FeeOption) => { + setSelectedOption(option); + if (!tips) return; + + const newTip = option === 'regular' ? tips.regularTip : tips.fastTip; + previousDefaultTip.current = newTip; + onFeeSelect?.(newTip); + }; + + if (isLoading) return ; + + const options = [ + { + id: 'regular', + name: 'Regular', + fee: tips ? fee.network.add(tips.regularTip) : fee.network, + }, + { + id: 'fast', + name: 'Fast', + fee: tips ? fee.network.add(tips.fastTip) : fee.network, + }, + ]; + return ( - onChecked?.(true)} - > - - {title || 'Fee (network)'} - - - {fee - ? `${fee.format({ - minPrecision: DEFAULT_PRECISION, - precision: DEFAULT_PRECISION, - })} ETH` - : '--'} - - + + + + {options.map((option) => ( + handleOptionSelect(option.id as FeeOption)} + > + handleOptionSelect(option.id as FeeOption)} + /> + + + {option.fee + ? `${option.fee.format({ + minPrecision: DEFAULT_PRECISION, + precision: DEFAULT_PRECISION, + })} ETH` + : '--'} + + + ))} + + + + + ); +} + +TxFee.Loader = function TxFeeLoader() { + return ( + + + + + + + + ); }; -TxFee.Loader = TxFeeLoader; +const styles = { + content: cssObj({ + display: 'flex', + flexDirection: 'column', + gap: '$3', + padding: '$3', + }), + option: cssObj({ + alignItems: 'center', + backgroundColor: '$white', + border: '1px solid #e0e0e0', + borderRadius: '10px', + color: '#646464', + cursor: 'pointer', + fontSize: '13px', + gap: '$3', + justifyContent: 'space-between', + padding: '$3', + transition: 'all 0.2s ease', + boxShadow: '0px 2px 6px -1px #2020201A, 0px 0px 0px 1px #2020201F', + + '&:hover': { + backgroundColor: '#f0f0f0', + }, + }), + optionContent: cssObj({ + color: '#202020', + }), + optionLabel: cssObj({ + color: '#202020', + fontSize: '13px', + fontWeight: '$medium', + }), + radio: cssObj({ + cursor: 'pointer', + height: '16px', + margin: 0, + width: '16px', + }), + customFeesBtn: cssObj({ + alignSelf: 'center', + color: '$accent11', + fontSize: '$sm', + mt: '$2', + }), +}; diff --git a/packages/app/src/systems/Transaction/components/TxFee/TxFeeLoader.tsx b/packages/app/src/systems/Transaction/components/TxFee/TxFeeLoader.tsx deleted file mode 100644 index 5500b28f0c..0000000000 --- a/packages/app/src/systems/Transaction/components/TxFee/TxFeeLoader.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import type { ContentLoaderProps } from '@fuel-ui/react'; -import { Card, ContentLoader, Text } from '@fuel-ui/react'; - -import { styles } from './styles'; - -export const TxFeeLoader = (props: ContentLoaderProps) => ( - - - Fee (network) - - - - - -); diff --git a/packages/app/src/systems/Transaction/components/TxFee/styles.tsx b/packages/app/src/systems/Transaction/components/TxFee/styles.tsx deleted file mode 100644 index 268e1c9c81..0000000000 --- a/packages/app/src/systems/Transaction/components/TxFee/styles.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; - -export const styles = { - detailItem: (active = false, pointer = false) => - cssObj({ - padding: '$3 $4', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - display: 'flex', - columnGap: '$4', - position: 'relative', - cursor: pointer ? 'pointer' : 'auto', - - ...(active && { - '&::after': { - position: 'absolute', - display: 'block', - content: '""', - top: 0, - left: 0, - width: '3px', - height: '100%', - background: '$intentsPrimary11', - borderRadius: '$md 0 0 $md', - }, - }), - }), - title: cssObj({ - fontSize: '$sm', - fontWeight: '$normal', - textWrap: 'nowrap', - }), - amount: cssObj({ - fontSize: '$sm', - fontWeight: '$normal', - wordWrap: 'break-word', - minWidth: 0, - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx b/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx index 3c5d032791..24611ad0ce 100644 --- a/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx +++ b/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx @@ -88,12 +88,6 @@ export const TxFeeOptions = ({ {isAdvanced ? ( - - From d49bdd5a5548df7b03b728ef09babf14b18872b6 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 17:26:24 -0300 Subject: [PATCH 74/85] merge: fee options --- .../components/TxContent/TxContent.tsx | 8 +- .../components/TxDetails/TxFeeOptions.tsx | 121 -------- .../components/TxFeeOptions/TxFeeOptions.tsx | 289 +++++++----------- 3 files changed, 109 insertions(+), 309 deletions(-) delete mode 100644 packages/app/src/systems/Transaction/components/TxDetails/TxFeeOptions.tsx diff --git a/packages/app/src/systems/Transaction/components/TxContent/TxContent.tsx b/packages/app/src/systems/Transaction/components/TxContent/TxContent.tsx index c4ecc7dfea..0079672ab3 100644 --- a/packages/app/src/systems/Transaction/components/TxContent/TxContent.tsx +++ b/packages/app/src/systems/Transaction/components/TxContent/TxContent.tsx @@ -117,7 +117,7 @@ function TxContentInfo({ const isExecuted = !!tx?.id; const txRequestGasLimit = getGasLimitFromTxRequest(txRequest); - const initialAdvanced = useMemo(() => { + const _initialAdvanced = useMemo(() => { if (!fees?.regularTip || !fees?.fastTip) return false; // it will start as advanced if the transaction tip is not equal to the regular tip and fast tip @@ -156,7 +156,7 @@ function TxContentInfo({ isLoading={isLoading} /> {isLoading && !showDetails && } - {showDetails && !fees && } + {/* {showDetails && !fees && } */} {showDetails && fees?.baseFee && txRequestGasLimit && @@ -164,13 +164,13 @@ function TxContentInfo({ fees?.fastTip && ( Fee (network) - + /> */} )} {footer} diff --git a/packages/app/src/systems/Transaction/components/TxDetails/TxFeeOptions.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxFeeOptions.tsx deleted file mode 100644 index ab405302e7..0000000000 --- a/packages/app/src/systems/Transaction/components/TxDetails/TxFeeOptions.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { Box, Button, HStack, Input, Text, VStack } from '@fuel-ui/react'; -import type { BN } from 'fuels'; -import { DEFAULT_PRECISION } from 'fuels'; -import { useState } from 'react'; -import { createAmount } from '~/systems/Core/components/InputAmount/InputAmount'; -import { isAmountAllowed } from '~/systems/Core/components/InputAmount/InputAmount.utils'; - -type TxFeeOptionsProps = { - baseFee: BN; - onBack: () => void; - onTipChange?: (tip: BN) => void; -}; - -const DECIMAL_UNITS = 9; - -export function TxFeeOptions({ - baseFee, - onBack, - onTipChange, -}: TxFeeOptionsProps) { - const [customTip, setCustomTip] = useState('0'); - - const handleTipChange = (text: string) => { - setCustomTip(text); - const { amount } = createAmount(text, DECIMAL_UNITS); - onTipChange?.(amount); - }; - - const totalFee = createAmount(customTip, DECIMAL_UNITS).amount.add(baseFee); - - return ( - - - - Fee + Tip - - - {' '} - ({totalFee.format({ minPrecision: DEFAULT_PRECISION })} ETH) - - - - - - - Gas limit - - - - - - Tip - - handleTipChange(e.target.value)} - /> - - - - - - ); -} - -const styles = { - content: cssObj({ - display: 'flex', - flexDirection: 'column', - gap: '$3', - padding: '$3', - }), - title: cssObj({ - fontSize: '$sm', - fontWeight: '$medium', - color: '$gray12', - }), - card: cssObj({ - padding: '$4', - backgroundColor: '$gray1', - border: '1px solid $gray6', - borderRadius: '10px', - fontSize: '13px', - }), - inputBox: cssObj({ - flex: 1, - display: 'flex', - flexDirection: 'column', - gap: '$2', - }), - input: cssObj({ - borderRadius: '8px', - backgroundColor: '$white', - border: '1px solid $gray5', - transition: 'border-color 0.3s', - '&:not([aria-disabled="true"]):focus-within': { - borderColor: '$brand', - }, - - '&>input': { - width: '100%', - }, - }), - backButton: cssObj({ - alignSelf: 'center', - color: '$accent11', - fontSize: '12px', - mt: '$2', - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx b/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx index 24611ad0ce..ab405302e7 100644 --- a/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx +++ b/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx @@ -1,200 +1,121 @@ -import { Box, Button, Form, HStack, Input, Text, VStack } from '@fuel-ui/react'; -import { AnimatePresence } from 'framer-motion'; -import { type BN, bn } from 'fuels'; -import { useEffect, useMemo, useRef, useState } from 'react'; -import { useController, useFormContext } from 'react-hook-form'; -import { MotionFlex, MotionStack, animations } from '~/systems/Core'; +import { cssObj } from '@fuel-ui/css'; +import { Box, Button, HStack, Input, Text, VStack } from '@fuel-ui/react'; +import type { BN } from 'fuels'; +import { DEFAULT_PRECISION } from 'fuels'; +import { useState } from 'react'; import { createAmount } from '~/systems/Core/components/InputAmount/InputAmount'; import { isAmountAllowed } from '~/systems/Core/components/InputAmount/InputAmount.utils'; -import type { SendFormValues } from '~/systems/Send/hooks'; -import { TxFee } from '../TxFee'; -import { - DECIMAL_UNITS, - formatTip, - isGasLimitAllowed, -} from './TxFeeOptions.utils'; type TxFeeOptionsProps = { - initialAdvanced: boolean; baseFee: BN; - gasLimit: BN; - regularTip: BN; - fastTip: BN; - onRecalculate?: (tip: BN) => void; + onBack: () => void; + onTipChange?: (tip: BN) => void; }; -export const TxFeeOptions = ({ - initialAdvanced, - baseFee, - gasLimit: gasLimitInput, - regularTip, - fastTip, - onRecalculate, -}: TxFeeOptionsProps) => { - const { control, setValue, getValues } = useFormContext(); - const [isAdvanced, setIsAdvanced] = useState(initialAdvanced); - const previousGasLimit = useRef(gasLimitInput); - const previousDefaultTip = useRef(regularTip); - - const { field: tip, fieldState: tipState } = useController({ - control, - name: 'fees.tip', - }); +const DECIMAL_UNITS = 9; - const { field: gasLimit, fieldState: gasLimitState } = useController({ - control, - name: 'fees.gasLimit', - }); - - const options = useMemo(() => { - return [ - { name: 'Regular', fee: baseFee.add(regularTip), tip: regularTip }, - { name: 'Fast', fee: baseFee.add(fastTip), tip: fastTip }, - ]; - }, [baseFee, regularTip, fastTip]); +export function TxFeeOptions({ + baseFee, + onBack, + onTipChange, +}: TxFeeOptionsProps) { + const [customTip, setCustomTip] = useState('0'); - const toggle = () => { - setIsAdvanced((curr) => !curr); + const handleTipChange = (text: string) => { + setCustomTip(text); + const { amount } = createAmount(text, DECIMAL_UNITS); + onTipChange?.(amount); }; - /** - * Resetting fees if hiding advanced options (or initializing them) - */ - useEffect(() => { - if (!isAdvanced) { - const [currentTip, currentGasLimit] = getValues([ - 'fees.tip.amount', - 'fees.gasLimit.amount', - ]); - - if (!currentGasLimit.eq(previousGasLimit.current)) { - setValue('fees.gasLimit', { - amount: previousGasLimit.current, - text: previousGasLimit.current.toString(), - }); - } - - if (!currentTip.eq(previousDefaultTip.current)) { - setValue('fees.tip', { - amount: previousDefaultTip.current, - text: formatTip(previousDefaultTip.current), - }); - } - } - }, [isAdvanced, setValue, getValues]); + const totalFee = createAmount(customTip, DECIMAL_UNITS).amount.add(baseFee); return ( - - - {isAdvanced ? ( - - - - - Gas limit - - - { - const val = e.target.value; + + + + Fee + Tip + + + {' '} + ({totalFee.format({ minPrecision: DEFAULT_PRECISION })} ETH) + + + + + + + Gas limit + + + + + + Tip + + handleTipChange(e.target.value)} + /> + + + + + + ); +} - gasLimit.onChange({ - amount: bn(val), - text: val, - }); - }} - /> - - - - - Tip - - - { - const text = e.target.value; - const { text: newText, amount } = createAmount( - text, - DECIMAL_UNITS - ); +const styles = { + content: cssObj({ + display: 'flex', + flexDirection: 'column', + gap: '$3', + padding: '$3', + }), + title: cssObj({ + fontSize: '$sm', + fontWeight: '$medium', + color: '$gray12', + }), + card: cssObj({ + padding: '$4', + backgroundColor: '$gray1', + border: '1px solid $gray6', + borderRadius: '10px', + fontSize: '13px', + }), + inputBox: cssObj({ + flex: 1, + display: 'flex', + flexDirection: 'column', + gap: '$2', + }), + input: cssObj({ + borderRadius: '8px', + backgroundColor: '$white', + border: '1px solid $gray5', + transition: 'border-color 0.3s', + '&:not([aria-disabled="true"]):focus-within': { + borderColor: '$brand', + }, - tip.onChange({ - amount, - text: newText, - }); - }} - /> - - - - - {(gasLimitState.error || tipState.error) && ( - - - - {gasLimitState.error?.message || tipState.error?.message} - - - - )} - - - ) : ( - - {options.map((option) => ( - { - previousDefaultTip.current = option.tip; - setValue('fees.tip', { - amount: option.tip, - text: formatTip(option.tip), - }); - onRecalculate?.(option.tip); - }} - /> - ))} - - )} - - - - - - ); + '&>input': { + width: '100%', + }, + }), + backButton: cssObj({ + alignSelf: 'center', + color: '$accent11', + fontSize: '12px', + mt: '$2', + }), }; From c29c74179163ee12e3c4aa483aa77d6070981269 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 17:54:21 -0300 Subject: [PATCH 75/85] fix: update import path for OperationDirection type --- packages/app/src/systems/Transaction/hooks/useTxMetadata.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/systems/Transaction/hooks/useTxMetadata.tsx b/packages/app/src/systems/Transaction/hooks/useTxMetadata.tsx index d9b5936eb4..d3e2ddaf0e 100644 --- a/packages/app/src/systems/Transaction/hooks/useTxMetadata.tsx +++ b/packages/app/src/systems/Transaction/hooks/useTxMetadata.tsx @@ -3,7 +3,7 @@ import { Address, OperationName } from 'fuels'; import { useMemo } from 'react'; import { useAccounts } from '~/systems/Account'; -import { OperationDirection } from '../types.tsx'; +import { OperationDirection } from '../types'; import { formatDate, getOperationDirection } from '../utils'; type UseTxMetadataProps = { From 955a90e035a46fd0ce2d0428344105ecd0384277 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 17:59:30 -0300 Subject: [PATCH 76/85] refactor: consolidate transaction operations components --- .../components/TxDetails/TxDetails.tsx | 6 +- .../TxOperationsSimple/TxOperationsList.tsx | 67 ------------- .../components/TxDetails/index.tsx | 1 - .../components/TxOperations/TxOperations.tsx | 98 +++++++++++-------- 4 files changed, 61 insertions(+), 111 deletions(-) delete mode 100644 packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationsList.tsx diff --git a/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx index 24ab0a3887..0e6ab79c2b 100644 --- a/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx +++ b/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx @@ -8,11 +8,11 @@ import type { TransactionSummary, } from 'fuels'; import { useState } from 'react'; -import { TxFeeOptions } from '.'; import { useSimplifiedTransaction } from '../../hooks/useSimplifiedTransaction'; import { TxFee } from '../TxFee'; +import { TxFeeOptions } from '../TxFeeOptions/TxFeeOptions'; +import { TxOperations } from '../TxOperations'; import { TxHeaderSimple } from './TxHeaderSimple'; -import { TxOperationsList } from './TxOperationsSimple/TxOperationsList'; export type TxViewVariant = 'default' | 'history'; @@ -48,7 +48,7 @@ export function TxDetails({ {!isHistory && } - + {showDetails && !isHistory && ( diff --git a/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationsList.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationsList.tsx deleted file mode 100644 index 52f2f29c0d..0000000000 --- a/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationsList.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { Box } from '@fuel-ui/react'; -import type { CategorizedOperations } from '../../../types'; -import { TxOperationsGroup } from './TxOperationsGroup'; -import { TxOperation } from './operations/TxOperation'; - -type TxOperationsListProps = { - operations: CategorizedOperations; -}; - -export function TxOperationsList({ operations }: TxOperationsListProps) { - const { mainOperations, otherRootOperations, intermediateOperations } = - operations; - - return ( - <> - {/* Main operations (transfers and root contract calls related to current account) */} - {mainOperations.map((operation, index) => ( - - - - ))} - - {/* Other root operations not related to current account */} - - - {/* Intermediate operations with nesting */} - {/* Intermediate opreations exist in the current setup? Wont they all be under the nesting of a root level operation? */} - - - ); -} - -TxOperationsList.Loader = function TxOperationsListLoader() { - return ( - - {[1, 2].map((i) => ( - - ))} - - ); -}; diff --git a/packages/app/src/systems/Transaction/components/TxDetails/index.tsx b/packages/app/src/systems/Transaction/components/TxDetails/index.tsx index 8d55c16e40..82117524c8 100644 --- a/packages/app/src/systems/Transaction/components/TxDetails/index.tsx +++ b/packages/app/src/systems/Transaction/components/TxDetails/index.tsx @@ -1,4 +1,3 @@ export * from './TxOperationsSimple'; export * from './TxHeaderSimple'; export * from './TxDetails'; -export * from './TxFeeOptions'; diff --git a/packages/app/src/systems/Transaction/components/TxOperations/TxOperations.tsx b/packages/app/src/systems/Transaction/components/TxOperations/TxOperations.tsx index 28200e7d6a..447e136695 100644 --- a/packages/app/src/systems/Transaction/components/TxOperations/TxOperations.tsx +++ b/packages/app/src/systems/Transaction/components/TxOperations/TxOperations.tsx @@ -1,49 +1,67 @@ -import { Alert, Box } from '@fuel-ui/react'; -import type { AssetData } from '@fuel-wallet/types'; -import type { Operation, TransactionStatus } from 'fuels'; -import type { Maybe } from '~/systems/Core'; +import { Box } from '@fuel-ui/react'; +import type { CategorizedOperations } from '../../types'; +import { TxOperationsGroup } from '../TxDetails/TxOperationsSimple/TxOperationsGroup'; +import { TxOperation } from '../TxDetails/TxOperationsSimple/operations/TxOperation'; -import { useNetworks } from '~/systems/Network'; -import { TxOperation } from '../TxOperation/TxOperation'; - -export type TxOperationsProps = { - operations?: Operation[]; - status?: Maybe; - isLoading?: boolean; +type TxOperationsListProps = { + operations: CategorizedOperations; }; -export function TxOperations({ - operations, - status, - isLoading, -}: TxOperationsProps) { - if (operations?.length === 0) { - return ( - - - No operations found in this transaction - - - ); - } +export function TxOperations({ operations }: TxOperationsListProps) { + const { mainOperations, otherRootOperations, intermediateOperations } = + operations; return ( - - {operations?.map((operation, index) => ( - - key={index} - operation={operation} - status={status} - isLoading={isLoading} - /> + <> + {/* Main operations (transfers and root contract calls related to current account) */} + {mainOperations.map((operation, index) => ( + + + ))} - + + {/* Other root operations not related to current account */} + + + {/* Intermediate operations with nesting */} + {/* Intermediate opreations exist in the current setup? Wont they all be under the nesting of a root level operation? */} + + ); } -TxOperations.Loader = () => ( - - - -); +TxOperations.Loader = function TxOperationsLoader() { + return ( + + {[1, 2].map((i) => ( + + ))} + + ); +}; From ca4476282488cfa39eee57113d15140d73ce5a63 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 18:02:24 -0300 Subject: [PATCH 77/85] fix: simplify transaction request loading state handling --- .../TransactionRequest/TransactionRequest.tsx | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx b/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx index 42e8125678..bf041624a3 100644 --- a/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx +++ b/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx @@ -5,11 +5,7 @@ import { useMemo } from 'react'; import { useAssets } from '~/systems/Asset'; import { Layout } from '~/systems/Core'; import { TopBarType } from '~/systems/Core/components/Layout/TopBar'; -import { - TxContent, - TxDetails, - getGasLimitFromTxRequest, -} from '~/systems/Transaction'; +import { TxDetails, getGasLimitFromTxRequest } from '~/systems/Transaction'; import { formatTip } from '~/systems/Transaction/components/TxFeeOptions/TxFeeOptions.utils'; import { useTransactionRequest } from '../../hooks/useTransactionRequest'; import { AutoSubmit } from './TransactionRequest.AutoSubmit'; @@ -60,17 +56,6 @@ export function TransactionRequest() { return status('loading') || status('sending') || isLoadingAssets; }, [status, isLoadingAssets]); - if (!defaultValues) { - return ( - - - - - - - ); - } - return ( )} {shouldShowTxExecuted && ( From f969d5188594443c0d6ddf235b9b0e41cba59c9a Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 18:07:14 -0300 Subject: [PATCH 78/85] refactor: rename transaction props for clarity and consistency --- .../TransactionRequest/TransactionRequest.tsx | 6 +++--- .../Transaction/components/TxDetails/TxDetails.tsx | 13 +++++++------ .../Transaction/hooks/useSimplifiedTransaction.tsx | 14 +++++++------- .../Transaction/pages/TxApprove/TxApprove.tsx | 4 ++-- .../systems/Transaction/pages/TxView/TxView.tsx | 2 +- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx b/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx index bf041624a3..22870fb713 100644 --- a/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx +++ b/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx @@ -73,15 +73,15 @@ export function TransactionRequest() { {shouldShowTxSimulated && ( )} {shouldShowTxExecuted && ( (); const isHistory = variant === 'history'; const { transaction, isReady } = useSimplifiedTransaction({ - summary, - request, + tx, + txRequest, }); if (!isReady || !transaction) return null; diff --git a/packages/app/src/systems/Transaction/hooks/useSimplifiedTransaction.tsx b/packages/app/src/systems/Transaction/hooks/useSimplifiedTransaction.tsx index 8e318cabc5..e1fdfd0d25 100644 --- a/packages/app/src/systems/Transaction/hooks/useSimplifiedTransaction.tsx +++ b/packages/app/src/systems/Transaction/hooks/useSimplifiedTransaction.tsx @@ -5,21 +5,21 @@ import type { SimplifiedTransaction } from '../types'; import { simplifyTransaction } from '../utils/simplifyTransaction'; type UseSimplifiedTransactionProps = { - summary?: TransactionSummary; - request?: TransactionRequest; + tx?: TransactionSummary; + txRequest?: TransactionRequest; }; export function useSimplifiedTransaction({ - summary, - request, + tx, + txRequest, }: UseSimplifiedTransactionProps) { const { account } = useAccounts(); const transaction = useMemo(() => { - if (!summary) return undefined; + if (!tx) return undefined; - return simplifyTransaction(summary, request, account?.address); - }, [summary, request, account?.address]); + return simplifyTransaction(tx, txRequest, account?.address); + }, [tx, txRequest, account?.address]); return { transaction, diff --git a/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx b/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx index dd64b3281e..9a71e55ac4 100644 --- a/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx +++ b/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx @@ -31,7 +31,7 @@ export const TxApprove = () => { {ctx.shouldShowTxSimulated && ( { )} {ctx.shouldShowTxExecuted && ( Date: Mon, 3 Feb 2025 18:12:00 -0300 Subject: [PATCH 79/85] refactor: remove TxContent component and related files --- .../TransactionRequest/TransactionRequest.tsx | 2 +- .../TxContent/TxContent.stories.tsx | 90 -------- .../components/TxContent/TxContent.tsx | 203 ------------------ .../components/TxContent/index.tsx | 1 - .../systems/Transaction/components/index.tsx | 1 - 5 files changed, 1 insertion(+), 296 deletions(-) delete mode 100644 packages/app/src/systems/Transaction/components/TxContent/TxContent.stories.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxContent/TxContent.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxContent/index.tsx diff --git a/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx b/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx index 22870fb713..067fdb2889 100644 --- a/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx +++ b/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx @@ -75,8 +75,8 @@ export function TransactionRequest() { )} {shouldShowTxExecuted && ( diff --git a/packages/app/src/systems/Transaction/components/TxContent/TxContent.stories.tsx b/packages/app/src/systems/Transaction/components/TxContent/TxContent.stories.tsx deleted file mode 100644 index 1388cefdc6..0000000000 --- a/packages/app/src/systems/Transaction/components/TxContent/TxContent.stories.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { Box, Button } from '@fuel-ui/react'; - -import type { Meta, StoryFn } from '@storybook/react'; -import { TransactionStatus, bn } from 'fuels'; -import { FormProvider, useForm } from 'react-hook-form'; -import type { SendFormValues } from '~/systems/Send/hooks'; -import { TxContent, type TxContentInfoProps } from './TxContent'; - -export default { - component: TxContent.Info, - title: 'Transaction/Components/TxContent', - decorators: [(Story) => ], - parameters: { - viewport: { - defaultViewport: 'chromeExtension', - }, - }, -} as Meta; - -const defaultArgs = { - tx: { - id: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', - }, - isLoading: false, - showDetails: true, - txStatus: TransactionStatus.success, - fees: { - baseFee: bn(0.01), - regularTip: bn(0.01), - fastTip: bn(0.01), - }, -} as TxContentInfoProps; - -const Template: StoryFn = (args) => { - const form = useForm({ - defaultValues: { - asset: 'BTC', - address: '0x1234567890abcdef1234567890abcdef12345678', - amount: bn(0.12345678), - fees: { - tip: { - amount: bn(0), - text: '0', - }, - gasLimit: { - amount: bn(0), - text: '0', - }, - }, - }, - }); - return ( - - - - - - ); -}; - -export const Default: StoryFn = Template.bind({}); -Default.args = { - ...defaultArgs, -} as TxContentInfoProps; - -export const Errors: StoryFn = Template.bind({}); -Errors.args = { - ...defaultArgs, - tx: undefined, - txStatus: TransactionStatus.failure, - errors: 'No assets available', - footer: ( - - ), -} as TxContentInfoProps; - -export const Loader = () => ( - - - -); diff --git a/packages/app/src/systems/Transaction/components/TxContent/TxContent.tsx b/packages/app/src/systems/Transaction/components/TxContent/TxContent.tsx deleted file mode 100644 index 0079672ab3..0000000000 --- a/packages/app/src/systems/Transaction/components/TxContent/TxContent.tsx +++ /dev/null @@ -1,203 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { - Alert, - Box, - CardList, - ContentLoader, - Copyable, - Icon, - Text, - VStack, -} from '@fuel-ui/react'; -import type { - BN, - TransactionRequest, - TransactionStatus, - TransactionSummary, -} from 'fuels'; -import { type ReactNode, useMemo } from 'react'; -import { useFormContext } from 'react-hook-form'; -import type { Maybe } from '~/systems/Core'; -import { MotionStack, animations } from '~/systems/Core'; -import type { SendFormValues } from '~/systems/Send/hooks'; -import { - type GroupedErrors, - TxFee, - TxHeader, - TxOperations, - getGasLimitFromTxRequest, -} from '~/systems/Transaction'; -import { TxFeeOptions } from '../TxFeeOptions/TxFeeOptions'; - -const ErrorHeader = ({ errors }: { errors?: GroupedErrors }) => { - return ( - - - - {errors} - - - - ); -}; - -const ConfirmHeader = () => ( - - - Carefully check if all the details in your transaction are correct - - -); - -const LoaderHeader = () => ( - - - - - -); - -function TxContentLoader() { - return ( - - - - - - ); -} - -export type TxContentInfoProps = { - footer?: ReactNode; - tx?: Maybe; - txStatus?: Maybe; - showDetails?: boolean; - isLoading?: boolean; - isConfirm?: boolean; - errors?: GroupedErrors; - fees?: { - baseFee?: BN; - regularTip?: BN; - fastTip?: BN; - }; - txRequest?: TransactionRequest; -}; - -function TxContentInfo({ - tx, - txStatus, - footer, - showDetails, - isLoading, - isConfirm, - errors, - fees, - txRequest, -}: TxContentInfoProps) { - const { getValues } = useFormContext(); - - const status = txStatus || tx?.status || txStatus; - const hasErrors = Boolean(Object.keys(errors || {}).length); - const isExecuted = !!tx?.id; - const txRequestGasLimit = getGasLimitFromTxRequest(txRequest); - - const _initialAdvanced = useMemo(() => { - if (!fees?.regularTip || !fees?.fastTip) return false; - - // it will start as advanced if the transaction tip is not equal to the regular tip and fast tip - const isFeeAmountTheRegularTip = getValues('fees.tip.amount').eq( - fees.regularTip - ); - const isFeeAmountTheFastTip = getValues('fees.tip.amount').eq(fees.fastTip); - // it will start as advanced if the gasLimit if different from the tx gasLimit - const isGasLimitTheTxRequestGasLimit = getValues('fees.gasLimit.amount').eq( - txRequestGasLimit - ); - - return ( - (!isFeeAmountTheRegularTip && !isFeeAmountTheFastTip) || - !isGasLimitTheTxRequestGasLimit - ); - }, [getValues, fees, txRequestGasLimit]); - - function getHeader() { - if (hasErrors) return ; - if (isConfirm) return ; - if (isExecuted) - return ( - - ); - - return ; - } - - return ( - - {getHeader()} - - {isLoading && !showDetails && } - {/* {showDetails && !fees && } */} - {showDetails && - fees?.baseFee && - txRequestGasLimit && - fees?.regularTip && - fees?.fastTip && ( - - Fee (network) - {/* */} - - )} - {footer} - - ); -} - -export const TxContent = { - Loader: TxContentLoader, - Info: TxContentInfo, -}; - -const styles = { - fees: cssObj({ - color: '$intentsBase12', - fontSize: '$md', - fontWeight: '$normal', - }), - alert: cssObj({ - '& .fuel_Alert-content': { - gap: '$1', - }, - ' & .fuel_Heading': { - fontSize: '$sm', - }, - '& .fuel_Icon': { - mt: '-2px', - }, - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxContent/index.tsx b/packages/app/src/systems/Transaction/components/TxContent/index.tsx deleted file mode 100644 index 6a894cc09b..0000000000 --- a/packages/app/src/systems/Transaction/components/TxContent/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export * from './TxContent'; diff --git a/packages/app/src/systems/Transaction/components/index.tsx b/packages/app/src/systems/Transaction/components/index.tsx index 414977cb82..31c5c6b4fe 100644 --- a/packages/app/src/systems/Transaction/components/index.tsx +++ b/packages/app/src/systems/Transaction/components/index.tsx @@ -1,4 +1,3 @@ -export * from './TxContent'; export * from './TxFee'; export * from './TxFromTo'; export * from './TxHeader'; From ef904dddacb484fc6aca1556136a571e195f2183 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 19:24:03 -0300 Subject: [PATCH 80/85] style: clean input styles to match current theme more --- .../components/TxFeeOptions/TxFeeOptions.tsx | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx b/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx index ab405302e7..6e136eec6d 100644 --- a/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx +++ b/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx @@ -45,20 +45,19 @@ export function TxFeeOptions({ Gas limit - + Tip - + input': { width: '100%', }, From 24a7671df33d578ba31d337d49979817bb323ef0 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 23:01:44 -0300 Subject: [PATCH 81/85] refactor: consolidate and simplify transaction details components --- .../TransactionRequest/TransactionRequest.tsx | 6 +- .../components/TxDetails/TxDetails.tsx | 149 ++++++-- .../components/TxDetails/TxHeaderSimple.tsx | 33 -- .../TxOperationsSimple/TxOperationsGroup.tsx | 2 +- .../operations/TxOperation.tsx | 156 -------- .../operations/TxOperationNesting.tsx | 32 -- .../components/TxDetails/index.tsx | 1 - .../components/TxFeeOptions/TxFeeOptions.tsx | 348 +++++++++++++++--- .../components/TxHeader/TxHeader.tsx | 127 ------- .../components/TxHeader/TxHeaderLoader.tsx | 17 - .../Transaction/components/TxHeader/index.tsx | 1 - .../components/TxOperation/TxOperation.tsx | 181 ++++++--- .../TxOperationCard.tsx | 3 +- .../components/TxOperations/TxOperations.tsx | 2 +- .../systems/Transaction/components/index.tsx | 1 - .../Transaction/pages/TxApprove/TxApprove.tsx | 1 + .../Transaction/pages/TxView/TxView.tsx | 17 +- 17 files changed, 575 insertions(+), 502 deletions(-) delete mode 100644 packages/app/src/systems/Transaction/components/TxDetails/TxHeaderSimple.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/operations/TxOperation.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/operations/TxOperationNesting.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxHeader/TxHeader.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxHeader/TxHeaderLoader.tsx delete mode 100644 packages/app/src/systems/Transaction/components/TxHeader/index.tsx rename packages/app/src/systems/Transaction/components/{TxDetails/TxOperationsSimple/operations => TxOperation}/TxOperationCard.tsx (98%) diff --git a/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx b/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx index 067fdb2889..0d026eb94d 100644 --- a/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx +++ b/packages/app/src/systems/DApp/pages/TransactionRequest/TransactionRequest.tsx @@ -28,6 +28,7 @@ export function TransactionRequest() { shouldShowTxExecuted, shouldShowActions, shouldDisableApproveBtn, + errors, proposedTxRequest, } = txRequest; const { isLoading: isLoadingAssets } = useAssets(); @@ -73,10 +74,13 @@ export function TransactionRequest() { {shouldShowTxSimulated && ( )} {shouldShowTxExecuted && ( diff --git a/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx index 6e93df42f5..1e1dbb0795 100644 --- a/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx +++ b/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx @@ -1,18 +1,58 @@ -import { log } from 'node:console'; import { cssObj } from '@fuel-ui/css'; -import { Box, Icon, Text } from '@fuel-ui/react'; +import { Alert, Box, Copyable, Icon, Text } from '@fuel-ui/react'; import type { BN, TransactionRequest, TransactionResult, TransactionSummary, } from 'fuels'; -import { useState } from 'react'; +import { useMemo, useState } from 'react'; +import { useFormContext } from 'react-hook-form'; +import type { SendFormValues } from '~/systems/Send/hooks'; +import { + type GroupedErrors, + getGasLimitFromTxRequest, +} from '~/systems/Transaction'; import { useSimplifiedTransaction } from '../../hooks/useSimplifiedTransaction'; import { TxFee } from '../TxFee'; import { TxFeeOptions } from '../TxFeeOptions/TxFeeOptions'; import { TxOperations } from '../TxOperations'; -import { TxHeaderSimple } from './TxHeaderSimple'; + +const ErrorHeader = ({ errors }: { errors?: GroupedErrors }) => { + return ( + + + + {errors} + + + + ); +}; + +const ConfirmHeader = () => ( + + {/* Disabled while the new Wallet header is not implemented */} + {/* Review Transaction */} + + + Double-check your transaction before submitting. + + +); export type TxViewVariant = 'default' | 'history'; @@ -23,6 +63,13 @@ type TxDetailsProps = { isLoading?: boolean; footer?: React.ReactNode; variant?: TxViewVariant; + errors?: GroupedErrors; + isConfirm?: boolean; + fees?: { + baseFee?: BN; + regularTip?: BN; + fastTip?: BN; + }; }; export function TxDetails({ @@ -32,11 +79,15 @@ export function TxDetails({ isLoading: externalLoading, footer, variant = 'default', + errors, + isConfirm, + fees, }: TxDetailsProps) { - // TODO: Handle errors.simulateTxErrors, isConfirm, fees - const [isCustomFees, setIsCustomFees] = useState(false); - const [_, setSelectedTip] = useState(); const isHistory = variant === 'history'; + const { getValues } = useFormContext(); + const hasErrors = Boolean(Object.keys(errors || {}).length); + const isExecuted = !!tx?.id; + const txRequestGasLimit = getGasLimitFromTxRequest(txRequest); const { transaction, isReady } = useSimplifiedTransaction({ tx, @@ -45,31 +96,57 @@ export function TxDetails({ if (!isReady || !transaction) return null; + const initialAdvanced = useMemo(() => { + if (!fees?.regularTip || !fees?.fastTip) return false; + + const isFeeAmountTheRegularTip = getValues('fees.tip.amount').eq( + fees.regularTip + ); + const isFeeAmountTheFastTip = getValues('fees.tip.amount').eq(fees.fastTip); + const isGasLimitTheTxRequestGasLimit = getValues('fees.gasLimit.amount').eq( + txRequestGasLimit + ); + + return ( + (!isFeeAmountTheRegularTip && !isFeeAmountTheFastTip) || + !isGasLimitTheTxRequestGasLimit + ); + }, [getValues, fees, txRequestGasLimit]); + + function getHeader() { + console.log('isHistory', isHistory, 'isExecuted', isExecuted); + if (hasErrors) return ; + if (isConfirm) return ; + if (isExecuted) return null; + return ; + } + return ( - {!isHistory && } + {getHeader()} - {showDetails && !isHistory && ( + {!isHistory && ( Fee (network) - {isCustomFees ? ( - setIsCustomFees(false)} - onTipChange={setSelectedTip} - /> - ) : ( - setIsCustomFees(true)} - onFeeSelect={setSelectedTip} - /> - )} + {externalLoading && !showDetails && } + {showDetails && !fees && } + {showDetails && + fees?.baseFee && + txRequestGasLimit && + fees?.regularTip && + fees?.fastTip && ( + + )} )} {footer} @@ -110,4 +187,30 @@ const styles = { strokeWidth: '2.5px', }, }), + alert: cssObj({ + '& .fuel_Alert-content': { + gap: '$1', + }, + ' & .fuel_Heading': { + fontSize: '$sm', + }, + '& .fuel_Icon': { + mt: '-2px', + }, + }), + header: cssObj({ + marginBottom: '$2', + backgroundColor: '$white', + borderBottom: '1px solid $gray3', + padding: '12px 18px', + }), + warning: cssObj({ + display: 'flex', + alignItems: 'center', + gap: '$1', + fontSize: '12px', + color: '$gray11', + fontWeight: '500', + marginBottom: '$2', + }), }; diff --git a/packages/app/src/systems/Transaction/components/TxDetails/TxHeaderSimple.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxHeaderSimple.tsx deleted file mode 100644 index 8169d0883c..0000000000 --- a/packages/app/src/systems/Transaction/components/TxDetails/TxHeaderSimple.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { Box, Icon } from '@fuel-ui/react'; - -export function TxHeaderSimple() { - return ( - - {/* Disabled while the new Wallet header is not implemented */} - {/* Review Transaction */} - - - Double-check your transaction before submitting. - - - ); -} - -const styles = { - header: cssObj({ - marginBottom: '$2', - backgroundColor: '$white', - borderBottom: '1px solid $gray3', - padding: '12px 18px', - }), - warning: cssObj({ - display: 'flex', - alignItems: 'center', - gap: '$1', - fontSize: '12px', - color: '$gray11', - fontWeight: '500', - marginBottom: '$2', - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationsGroup.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationsGroup.tsx index ea7a2dd541..f43d3230dd 100644 --- a/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationsGroup.tsx +++ b/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationsGroup.tsx @@ -3,7 +3,7 @@ import { Box, Icon, Text } from '@fuel-ui/react'; import { useState } from 'react'; import { MotionBox } from '~/systems/Core/components/Motion'; import type { SimplifiedOperation } from '../../../types'; -import { TxOperation } from './operations/TxOperation'; +import { TxOperation } from '../../TxOperation'; type TxOperationsGroupProps = { title: string; diff --git a/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/operations/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/operations/TxOperation.tsx deleted file mode 100644 index addf0b376c..0000000000 --- a/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/operations/TxOperation.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { Box, Icon, Text } from '@fuel-ui/react'; -import type { AssetFuelAmount } from '@fuel-wallet/types'; -import { useEffect, useState } from 'react'; -import { AssetsCache } from '~/systems/Asset/cache/AssetsCache'; -import { MotionBox, animations } from '~/systems/Core'; -import { NetworkService } from '~/systems/Network/services/network'; -import { TxCategory } from '../../../../types'; -import type { SimplifiedOperation } from '../../../../types'; -import { TxOperationCard } from './TxOperationCard'; - -type TxOperationProps = { - operation: SimplifiedOperation; - showNesting?: boolean; -}; - -const fetchAssetsAmount = async (operation: SimplifiedOperation) => { - try { - const coins = []; - if (operation.amount && operation.assetId) { - coins.push({ - amount: operation.amount, - assetId: operation.assetId, - }); - } else if (operation.metadata?.amount && operation.metadata?.assetId) { - coins.push({ - amount: operation.metadata.amount, - assetId: operation.metadata.assetId, - }); - } - - if (!coins.length) return []; - - const assetsCache = AssetsCache.getInstance(); - const network = await NetworkService.getSelectedNetwork(); - if (!network) return []; - - const assetsWithAmount = await Promise.all( - coins.map(async (operationCoin) => { - const assetCached = await assetsCache.getAsset({ - chainId: network.chainId, - assetId: operationCoin.assetId, - dbAssets: [], - save: false, - }); - - if (!assetCached) return null; - - return { - type: 'fuel', - chainId: network.chainId, - name: assetCached.name, - symbol: assetCached.symbol, - decimals: assetCached.decimals, - icon: assetCached.icon, - assetId: operationCoin.assetId, - amount: operationCoin.amount, - } as AssetFuelAmount; - }) - ); - - const filteredAssets = assetsWithAmount.filter( - (a): a is AssetFuelAmount => a !== null - ); - return filteredAssets; - } catch (error) { - console.error('Error fetching assets:', error); - return []; - } -}; - -export function TxOperation({ - operation, - // showNesting = true, -}: TxOperationProps) { - const metadata = operation.metadata; - // const isContract = operation.type === TxCategory.CONTRACTCALL; - const depth = metadata?.depth || 0; - const [assetsAmount, setAssetsAmount] = useState([]); - const [isExpanded, setIsExpanded] = useState(false); - useEffect(() => { - fetchAssetsAmount(operation).then(setAssetsAmount); - }, [operation]); - - // if (isContract && !showNesting && depth !== 0) return null; - - return ( - - - {metadata.operationCount && metadata.operationCount > 1 && ( - setIsExpanded(!isExpanded)} - > - - - {isExpanded ? 'Collapse' : 'Expand'} - - {isExpanded ? null : ( - - (+{metadata.operationCount} operations) - - )} - - )} - {isExpanded && ( - - {metadata.childOperations?.map((op, idx) => ( - - ))} - - )} - - ); -} - -const styles = { - contentCol: cssObj({ - display: 'flex', - backgroundColor: '$gray1', - boxShadow: '0px 2px 6px -1px $colors$gray4, 0px 0px 0px 1px $colors$gray6', - flex: 1, - borderRadius: '8px', - minWidth: 0, - padding: '14px 12px', - margin: '0 4px', - }), - operationCount: { - marginTop: '$2', - marginLeft: '$2', - display: 'flex', - alignItems: 'center', - gap: '$1', - justifyContent: 'center', - marginBottom: '$2', - cursor: 'pointer', - }, - expandedOperations: cssObj({ - display: 'flex', - flexDirection: 'column', - gap: '$1', - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/operations/TxOperationNesting.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/operations/TxOperationNesting.tsx deleted file mode 100644 index 84dfdbbdc2..0000000000 --- a/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/operations/TxOperationNesting.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { Box } from '@fuel-ui/react'; - -type TxOperationNestingProps = { - depth: number; -}; - -export function TxOperationNesting({ depth }: TxOperationNestingProps) { - if (depth === 0) return null; - - return ( - - {Array.from({ length: depth }).map((_, _i) => ( - - ))} - - ); -} - -const styles = { - root: cssObj({ - display: 'flex', - alignItems: 'center', - gap: '$1', - marginRight: '$2', - }), - line: cssObj({ - width: '12px', - height: '100%', - borderLeft: '1px solid $gray4', - }), -}; diff --git a/packages/app/src/systems/Transaction/components/TxDetails/index.tsx b/packages/app/src/systems/Transaction/components/TxDetails/index.tsx index 82117524c8..28af7d5d38 100644 --- a/packages/app/src/systems/Transaction/components/TxDetails/index.tsx +++ b/packages/app/src/systems/Transaction/components/TxDetails/index.tsx @@ -1,3 +1,2 @@ export * from './TxOperationsSimple'; -export * from './TxHeaderSimple'; export * from './TxDetails'; diff --git a/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx b/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx index 6e136eec6d..ca1c311c46 100644 --- a/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx +++ b/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx @@ -1,89 +1,321 @@ import { cssObj } from '@fuel-ui/css'; -import { Box, Button, HStack, Input, Text, VStack } from '@fuel-ui/react'; +import { + Box, + Button, + Form, + HStack, + Input, + RadioGroup, + RadioGroupItem, + Text, +} from '@fuel-ui/react'; +import { AnimatePresence } from 'framer-motion'; import type { BN } from 'fuels'; -import { DEFAULT_PRECISION } from 'fuels'; -import { useState } from 'react'; +import { DEFAULT_PRECISION, bn } from 'fuels'; +import { useEffect, useMemo, useRef, useState } from 'react'; +import { useController, useFormContext } from 'react-hook-form'; +import { MotionFlex, MotionStack, animations } from '~/systems/Core'; import { createAmount } from '~/systems/Core/components/InputAmount/InputAmount'; import { isAmountAllowed } from '~/systems/Core/components/InputAmount/InputAmount.utils'; +import type { SendFormValues } from '~/systems/Send/hooks'; +import { + DECIMAL_UNITS, + formatTip, + isGasLimitAllowed, +} from './TxFeeOptions.utils'; type TxFeeOptionsProps = { + initialAdvanced: boolean; baseFee: BN; - onBack: () => void; - onTipChange?: (tip: BN) => void; + gasLimit: BN; + regularTip: BN; + fastTip: BN; + onRecalculate?: (tip: BN) => void; }; -const DECIMAL_UNITS = 9; - export function TxFeeOptions({ + initialAdvanced, baseFee, - onBack, - onTipChange, + gasLimit: gasLimitInput, + regularTip, + fastTip, + onRecalculate, }: TxFeeOptionsProps) { - const [customTip, setCustomTip] = useState('0'); + const { control, setValue, getValues } = useFormContext(); + const [isAdvanced, setIsAdvanced] = useState(initialAdvanced); + const previousGasLimit = useRef(gasLimitInput); + const previousDefaultTip = useRef(regularTip); + + const { field: tip, fieldState: tipState } = useController({ + control, + name: 'fees.tip', + }); + + const { field: gasLimit, fieldState: gasLimitState } = useController({ + control, + name: 'fees.gasLimit', + }); + + const advancedFee = baseFee.add(tip.value.amount); + + const options = useMemo(() => { + return [ + { + id: 'regular', + name: 'Regular', + fee: baseFee.add(regularTip), + tip: regularTip, + }, + { id: 'fast', name: 'Fast', fee: baseFee.add(fastTip), tip: fastTip }, + ]; + }, [baseFee, regularTip, fastTip]); - const handleTipChange = (text: string) => { - setCustomTip(text); - const { amount } = createAmount(text, DECIMAL_UNITS); - onTipChange?.(amount); + const toggle = () => { + setIsAdvanced((curr) => !curr); }; - const totalFee = createAmount(customTip, DECIMAL_UNITS).amount.add(baseFee); + /** + * Resetting fees if hiding advanced options (or initializing them) + */ + useEffect(() => { + if (!isAdvanced) { + const [currentTip, currentGasLimit] = getValues([ + 'fees.tip.amount', + 'fees.gasLimit.amount', + ]); + + if (!currentGasLimit.eq(previousGasLimit.current)) { + setValue('fees.gasLimit', { + amount: previousGasLimit.current, + text: previousGasLimit.current.toString(), + }); + } + + if (!currentTip.eq(previousDefaultTip.current)) { + setValue('fees.tip', { + amount: previousDefaultTip.current, + text: formatTip(previousDefaultTip.current), + }); + } + } + }, [isAdvanced, setValue, getValues]); return ( - - - - Fee + Tip - - - {' '} - ({totalFee.format({ minPrecision: DEFAULT_PRECISION })} ETH) - - - - - - - Gas limit - - - - - - Tip - - handleTipChange(e.target.value)} - /> - - - - - + + + {isAdvanced ? ( + + + + + Fee + Tip + + + ( + {advancedFee + ? `${advancedFee.format({ + minPrecision: DEFAULT_PRECISION, + precision: DEFAULT_PRECISION, + })} ETH` + : '--'} + ) + + + + + + + Gas limit + + + { + const val = e.target.value; + + gasLimit.onChange({ + amount: bn(val), + text: val, + }); + }} + /> + + + + + Tip + + + { + const text = e.target.value; + const { text: newText, amount } = createAmount( + text, + DECIMAL_UNITS + ); + + tip.onChange({ + amount, + text: newText, + }); + }} + /> + + + + + {(gasLimitState.error || tipState.error) && ( + + + + {gasLimitState.error?.message || tipState.error?.message} + + + + )} + + + ) : ( + + + {options.map((option) => ( + + { + previousDefaultTip.current = option.tip; + setValue('fees.tip', { + amount: option.tip, + text: formatTip(option.tip), + }); + onRecalculate?.(option.tip); + }} + /> + + + {option.fee + ? `${option.fee.format({ + minPrecision: DEFAULT_PRECISION, + precision: DEFAULT_PRECISION, + })} ETH` + : '--'} + + + ))} + + + )} + + + + + ); } const styles = { + detailItem: (checked: boolean, clickable: boolean) => + cssObj({ + padding: '$4', + display: 'flex', + flexDirection: 'column', + gap: '$1', + cursor: clickable ? 'pointer' : 'default', + backgroundColor: checked ? '$accent3' : '$gray1', + border: '1px solid', + borderColor: checked ? '$accent7' : '$gray6', + transition: 'all 0.2s ease', + + '&:hover': clickable + ? { + backgroundColor: checked ? '$accent4' : '$gray4', + borderColor: checked ? '$accent8' : '$gray8', + } + : {}, + }), + title: cssObj({ + fontSize: '$sm', + fontWeight: '$normal', + }), + amount: cssObj({ + fontSize: '$sm', + fontWeight: '$semibold', + }), content: cssObj({ display: 'flex', flexDirection: 'column', gap: '$3', padding: '$3', }), - title: cssObj({ - fontSize: '$sm', + option: cssObj({ + alignItems: 'center', + backgroundColor: '$white', + border: '1px solid #e0e0e0', + borderRadius: '10px', + color: '#646464', + cursor: 'pointer', + fontSize: '13px', + gap: '$3', + justifyContent: 'space-between', + padding: '$3', + transition: 'all 0.2s ease', + + '&:hover': { + backgroundColor: '#f0f0f0', + }, + }), + optionContent: cssObj({ + color: '#202020', + }), + optionLabel: cssObj({ + color: '#202020', + fontSize: '13px', fontWeight: '$medium', - color: '$gray12', + }), + radio: cssObj({ + cursor: 'pointer', + height: '16px', + margin: 0, + width: '16px', + }), + customFeesBtn: cssObj({ + alignSelf: 'center', + color: '$accent11', + fontSize: '$sm', + mt: '$2', }), card: cssObj({ padding: '$4', diff --git a/packages/app/src/systems/Transaction/components/TxHeader/TxHeader.tsx b/packages/app/src/systems/Transaction/components/TxHeader/TxHeader.tsx deleted file mode 100644 index 71a2898687..0000000000 --- a/packages/app/src/systems/Transaction/components/TxHeader/TxHeader.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { cssObj } from '@fuel-ui/css'; -import { - Box, - Card, - Copyable, - HStack, - Icon, - Text, - Tooltip, -} from '@fuel-ui/react'; -import { TransactionStatus } from 'fuels'; -import type { TransactionTypeName } from 'fuels'; -import type { FC } from 'react'; - -import { useExplorerLink } from '../../hooks/useExplorerLink'; -import { getTxStatusColor } from '../../utils'; - -import { shortAddress } from '~/systems/Core'; -import { TxHeaderLoader } from './TxHeaderLoader'; - -export type TxHeaderProps = { - status?: TransactionStatus; - id?: string; - type?: TransactionTypeName; -}; - -type TxHeaderComponent = FC & { - Loader: typeof TxHeaderLoader; -}; - -export const TxHeader: TxHeaderComponent = ({ status, id, type }) => { - const { href, openExplorer } = useExplorerLink(id); - - return ( - - - - ID: - - {shortAddress(id)} - - - - - {href && ( - <> - - - - - - )} - - - - Status: - - {status} - - - - - Type: - - {type} - - - - ); -}; - -const styles = { - root: cssObj({ - px: '$4', - py: '$3', - fontWeight: '$normal', - - '.fuel_copyable-icon': { - color: '$brand !important', - }, - }), - circle: cssObj({ - borderRadius: '100%', - width: 6, - height: 6, - cursor: 'default', - - [`&[data-status="${TransactionStatus.success}"]`]: { - backgroundColor: `$${getTxStatusColor(TransactionStatus.success)}`, - }, - [`&[data-status="${TransactionStatus.failure}"]`]: { - backgroundColor: `$${getTxStatusColor(TransactionStatus.failure)}`, - }, - [`&[data-status="${TransactionStatus.submitted}"]`]: { - backgroundColor: `$${getTxStatusColor(TransactionStatus.submitted)}`, - }, - }), - icon: cssObj({ - cursor: 'pointer', - }), -}; - -TxHeader.Loader = TxHeaderLoader; diff --git a/packages/app/src/systems/Transaction/components/TxHeader/TxHeaderLoader.tsx b/packages/app/src/systems/Transaction/components/TxHeader/TxHeaderLoader.tsx deleted file mode 100644 index a9545e34a9..0000000000 --- a/packages/app/src/systems/Transaction/components/TxHeader/TxHeaderLoader.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { ContentLoaderProps } from '@fuel-ui/react'; -import { Card, ContentLoader } from '@fuel-ui/react'; - -export const TxHeaderLoader = (props: ContentLoaderProps) => ( - - - - - - - -); diff --git a/packages/app/src/systems/Transaction/components/TxHeader/index.tsx b/packages/app/src/systems/Transaction/components/TxHeader/index.tsx deleted file mode 100644 index def37b9adc..0000000000 --- a/packages/app/src/systems/Transaction/components/TxHeader/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export * from './TxHeader'; diff --git a/packages/app/src/systems/Transaction/components/TxOperation/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxOperation/TxOperation.tsx index 16c3eda153..1d6faf0ea6 100644 --- a/packages/app/src/systems/Transaction/components/TxOperation/TxOperation.tsx +++ b/packages/app/src/systems/Transaction/components/TxOperation/TxOperation.tsx @@ -1,58 +1,155 @@ import { cssObj } from '@fuel-ui/css'; -import { Card } from '@fuel-ui/react'; -import type { AssetData } from '@fuel-wallet/types'; -import { type Operation, type TransactionStatus, getAssetFuel } from 'fuels'; -import { AssetsAmount } from '~/systems/Asset'; -import type { Maybe } from '~/systems/Core'; - -import { useAssetsAmount } from '../../hooks/useAssetsAmount'; -import { FunctionCalls } from '../FunctionCalls'; -import { TxFromTo } from '../TxFromTo/TxFromTo'; - -export type TxOperationProps = { - operation?: Operation; - status?: Maybe; - assets?: Maybe; - isLoading?: boolean; +import { Box, Icon, Text } from '@fuel-ui/react'; +import type { AssetFuelAmount } from '@fuel-wallet/types'; +import { useEffect, useState } from 'react'; +import { AssetsCache } from '~/systems/Asset/cache/AssetsCache'; +import { MotionBox, animations } from '~/systems/Core'; +import { NetworkService } from '~/systems/Network/services/network'; +import type { SimplifiedOperation } from '../../types'; +import { TxOperationCard } from './TxOperationCard'; + +type TxOperationProps = { + operation: SimplifiedOperation; + showNesting?: boolean; +}; + +const fetchAssetsAmount = async (operation: SimplifiedOperation) => { + try { + const coins = []; + if (operation.amount && operation.assetId) { + coins.push({ + amount: operation.amount, + assetId: operation.assetId, + }); + } else if (operation.metadata?.amount && operation.metadata?.assetId) { + coins.push({ + amount: operation.metadata.amount, + assetId: operation.metadata.assetId, + }); + } + + if (!coins.length) return []; + + const assetsCache = AssetsCache.getInstance(); + const network = await NetworkService.getSelectedNetwork(); + if (!network) return []; + + const assetsWithAmount = await Promise.all( + coins.map(async (operationCoin) => { + const assetCached = await assetsCache.getAsset({ + chainId: network.chainId, + assetId: operationCoin.assetId, + dbAssets: [], + save: false, + }); + + if (!assetCached) return null; + + return { + type: 'fuel', + chainId: network.chainId, + name: assetCached.name, + symbol: assetCached.symbol, + decimals: assetCached.decimals, + icon: assetCached.icon, + assetId: operationCoin.assetId, + amount: operationCoin.amount, + } as AssetFuelAmount; + }) + ); + + const filteredAssets = assetsWithAmount.filter( + (a): a is AssetFuelAmount => a !== null + ); + return filteredAssets; + } catch (error) { + console.error('Error fetching assets:', error); + return []; + } }; export function TxOperation({ operation, - status, - isLoading, + // showNesting = true, }: TxOperationProps) { - const { from, to, assetsSent, calls } = operation ?? {}; - const amounts = useAssetsAmount({ - operationsCoin: assetsSent, - }); + const metadata = operation.metadata; + // const isContract = operation.type === TxCategory.CONTRACTCALL; + const depth = metadata?.depth || 0; + const [assetsAmount, setAssetsAmount] = useState([]); + const [isExpanded, setIsExpanded] = useState(false); + useEffect(() => { + fetchAssetsAmount(operation).then(setAssetsAmount); + }, [operation]); + + // if (isContract && !showNesting && depth !== 0) return null; + return ( - - + - {!!amounts?.length && } - {!!calls?.length && } - + {metadata.operationCount && metadata.operationCount > 1 && ( + setIsExpanded(!isExpanded)} + > + + + {isExpanded ? 'Collapse' : 'Expand'} + + {isExpanded ? null : ( + + (+{metadata.operationCount} operations) + + )} + + )} + {isExpanded && ( + + {metadata.childOperations?.map((op, idx) => ( + + ))} + + )} + ); } -TxOperation.Loader = () => ( - - - - -); - const styles = { - root: cssObj({ - borderRadius: '$md', - overflow: 'hidden', - position: 'relative', + contentCol: cssObj({ + display: 'flex', + backgroundColor: '$gray1', + boxShadow: '0px 2px 6px -1px $colors$gray4, 0px 0px 0px 1px $colors$gray6', + flex: 1, + borderRadius: '8px', + minWidth: 0, + padding: '14px 12px', + margin: '0 4px', + }), + operationCount: { + marginTop: '$2', + marginLeft: '$2', + display: 'flex', + alignItems: 'center', + gap: '$1', + justifyContent: 'center', + marginBottom: '$2', + cursor: 'pointer', + }, + expandedOperations: cssObj({ display: 'flex', flexDirection: 'column', + gap: '$1', }), }; diff --git a/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/operations/TxOperationCard.tsx b/packages/app/src/systems/Transaction/components/TxOperation/TxOperationCard.tsx similarity index 98% rename from packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/operations/TxOperationCard.tsx rename to packages/app/src/systems/Transaction/components/TxOperation/TxOperationCard.tsx index 41ed969991..ea50b33abf 100644 --- a/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/operations/TxOperationCard.tsx +++ b/packages/app/src/systems/Transaction/components/TxOperation/TxOperationCard.tsx @@ -12,8 +12,7 @@ import type { AssetFuelAmount } from '@fuel-wallet/types'; import { bn } from 'fuels'; import { useAccounts } from '~/systems/Account'; import { formatAmount, shortAddress } from '~/systems/Core'; -import type { SimplifiedOperation } from '../../../../types'; -import { TxCategory } from '../../../../types'; +import { type SimplifiedOperation, TxCategory } from '../../types'; type TxOperationCardProps = { operation: SimplifiedOperation; diff --git a/packages/app/src/systems/Transaction/components/TxOperations/TxOperations.tsx b/packages/app/src/systems/Transaction/components/TxOperations/TxOperations.tsx index 447e136695..4e342332df 100644 --- a/packages/app/src/systems/Transaction/components/TxOperations/TxOperations.tsx +++ b/packages/app/src/systems/Transaction/components/TxOperations/TxOperations.tsx @@ -1,7 +1,7 @@ import { Box } from '@fuel-ui/react'; import type { CategorizedOperations } from '../../types'; import { TxOperationsGroup } from '../TxDetails/TxOperationsSimple/TxOperationsGroup'; -import { TxOperation } from '../TxDetails/TxOperationsSimple/operations/TxOperation'; +import { TxOperation } from '../TxOperation'; type TxOperationsListProps = { operations: CategorizedOperations; diff --git a/packages/app/src/systems/Transaction/components/index.tsx b/packages/app/src/systems/Transaction/components/index.tsx index 31c5c6b4fe..e727486ebb 100644 --- a/packages/app/src/systems/Transaction/components/index.tsx +++ b/packages/app/src/systems/Transaction/components/index.tsx @@ -1,6 +1,5 @@ export * from './TxFee'; export * from './TxFromTo'; -export * from './TxHeader'; export * from './TxLink'; export * from './TxOperation'; export * from './TxOperations'; diff --git a/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx b/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx index 9a71e55ac4..ad0c38c6b9 100644 --- a/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx +++ b/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx @@ -47,6 +47,7 @@ export const TxApprove = () => { ) } + isConfirm /> )} {ctx.shouldShowTxExecuted && ( diff --git a/packages/app/src/systems/Transaction/pages/TxView/TxView.tsx b/packages/app/src/systems/Transaction/pages/TxView/TxView.tsx index 2023da4b9a..91fa0be000 100644 --- a/packages/app/src/systems/Transaction/pages/TxView/TxView.tsx +++ b/packages/app/src/systems/Transaction/pages/TxView/TxView.tsx @@ -1,6 +1,8 @@ +import { FormProvider, useForm } from 'react-hook-form'; import { useNavigate, useParams } from 'react-router-dom'; import { Layout } from '~/systems/Core'; import { useNetworks } from '~/systems/Network'; +import type { SendFormValues } from '~/systems/Send/hooks'; import { TxStatusAlert } from '../../components'; import { TxDetails } from '../../components/TxDetails/TxDetails'; import { useTxResult } from '../../hooks'; @@ -15,6 +17,7 @@ export function TxView() { txId: txIdQueryParam, waitProviderUrl: true, }); + const form = useForm(); return ( )} {txResult && ( - + + + )} From 1a1ad7e4af3329fde7fefa86e1a2a723bd428784 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 23:29:26 -0300 Subject: [PATCH 82/85] refactor: remove transaction view variant and simplify TxDetails props --- .../components/TxDetails/TxDetails.tsx | 54 +++++++++---------- .../Transaction/pages/TxApprove/TxApprove.tsx | 10 ++-- .../Transaction/pages/TxView/TxView.tsx | 3 +- .../app/src/systems/Transaction/types.tsx | 7 --- 4 files changed, 29 insertions(+), 45 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx index 1e1dbb0795..472084da51 100644 --- a/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx +++ b/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx @@ -62,7 +62,6 @@ type TxDetailsProps = { showDetails?: boolean; isLoading?: boolean; footer?: React.ReactNode; - variant?: TxViewVariant; errors?: GroupedErrors; isConfirm?: boolean; fees?: { @@ -75,15 +74,13 @@ type TxDetailsProps = { export function TxDetails({ tx, txRequest, - showDetails = true, - isLoading: externalLoading, + showDetails, + isLoading, footer, - variant = 'default', errors, isConfirm, fees, }: TxDetailsProps) { - const isHistory = variant === 'history'; const { getValues } = useFormContext(); const hasErrors = Boolean(Object.keys(errors || {}).length); const isExecuted = !!tx?.id; @@ -114,7 +111,6 @@ export function TxDetails({ }, [getValues, fees, txRequestGasLimit]); function getHeader() { - console.log('isHistory', isHistory, 'isExecuted', isExecuted); if (hasErrors) return ; if (isConfirm) return ; if (isExecuted) return null; @@ -126,31 +122,29 @@ export function TxDetails({ {getHeader()} - {!isHistory && ( - - - - Fee (network) - - {externalLoading && !showDetails && } - {showDetails && !fees && } - {showDetails && - fees?.baseFee && - txRequestGasLimit && - fees?.regularTip && - fees?.fastTip && ( - - )} - - )} - {footer} + {isLoading && !showDetails && } + {showDetails && !fees && } + {showDetails && + fees?.baseFee && + txRequestGasLimit && + fees?.regularTip && + fees?.fastTip && ( + + + + Fee (network) + + + + )} + {footer} ); } diff --git a/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx b/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx index ad0c38c6b9..23637f33ec 100644 --- a/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx +++ b/packages/app/src/systems/Transaction/pages/TxApprove/TxApprove.tsx @@ -31,10 +31,10 @@ export const TxApprove = () => { {ctx.shouldShowTxSimulated && ( { )} {ctx.shouldShowTxExecuted && ( )} diff --git a/packages/app/src/systems/Transaction/types.tsx b/packages/app/src/systems/Transaction/types.tsx index 337a7b1694..3f714b38f0 100644 --- a/packages/app/src/systems/Transaction/types.tsx +++ b/packages/app/src/systems/Transaction/types.tsx @@ -133,13 +133,6 @@ export type SimplifiedTransaction = { }; }; -export type SimplifiedTransactionViewProps = { - transaction: SimplifiedTransaction; - showDetails?: boolean; - isLoading?: boolean; - footer?: ReactNode; -}; - export interface AssetFlow { assetId: string; amount: BN; From 2073308a8a4eb91a7792ffcbec71212b3b112c1c Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Mon, 3 Feb 2025 23:48:37 -0300 Subject: [PATCH 83/85] fix: correctly display total transaction fee --- .../components/TxDetails/TxDetails.tsx | 2 +- .../Transaction/components/TxFee/TxFeeLoader.tsx | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 packages/app/src/systems/Transaction/components/TxFee/TxFeeLoader.tsx diff --git a/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx index 472084da51..e5cb66e892 100644 --- a/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx +++ b/packages/app/src/systems/Transaction/components/TxDetails/TxDetails.tsx @@ -123,7 +123,7 @@ export function TxDetails({ {isLoading && !showDetails && } - {showDetails && !fees && } + {showDetails && !fees && } {showDetails && fees?.baseFee && txRequestGasLimit && diff --git a/packages/app/src/systems/Transaction/components/TxFee/TxFeeLoader.tsx b/packages/app/src/systems/Transaction/components/TxFee/TxFeeLoader.tsx new file mode 100644 index 0000000000..5500b28f0c --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxFee/TxFeeLoader.tsx @@ -0,0 +1,15 @@ +import type { ContentLoaderProps } from '@fuel-ui/react'; +import { Card, ContentLoader, Text } from '@fuel-ui/react'; + +import { styles } from './styles'; + +export const TxFeeLoader = (props: ContentLoaderProps) => ( + + + Fee (network) + + + + + +); From 30033aaece6fee79772aac70777cdacd692fe4f9 Mon Sep 17 00:00:00 2001 From: Nelito Junior Date: Tue, 4 Feb 2025 00:33:02 -0300 Subject: [PATCH 84/85] feat: improve transaction fee selection with radio group --- .../systems/Core/components/Layout/Layout.tsx | 3 +- .../OverlayDialog/OverlayDialog.tsx | 2 +- .../components/TxFee/TxFeeRadio.tsx | 92 +++++++++++++++++++ .../Transaction/components/TxFee/styles.tsx | 25 +++++ .../components/TxFeeOptions/TxFeeOptions.tsx | 54 +++++++---- 5 files changed, 156 insertions(+), 20 deletions(-) create mode 100644 packages/app/src/systems/Transaction/components/TxFee/TxFeeRadio.tsx diff --git a/packages/app/src/systems/Core/components/Layout/Layout.tsx b/packages/app/src/systems/Core/components/Layout/Layout.tsx index efb7bbe7c4..1f95d9f722 100644 --- a/packages/app/src/systems/Core/components/Layout/Layout.tsx +++ b/packages/app/src/systems/Core/components/Layout/Layout.tsx @@ -149,8 +149,9 @@ export const styles = { padding: '$0 $4 $4 $4', flex: 1, '&[data-scrollable=true]:not([data-noborder])': { - padding: '$0', + padding: '$0 $0 $4 $4', ...coreStyles.scrollable(), + overflowY: 'scroll !important', }, '&[data-noborder]': { padding: '$0', diff --git a/packages/app/src/systems/Overlay/components/OverlayDialog/OverlayDialog.tsx b/packages/app/src/systems/Overlay/components/OverlayDialog/OverlayDialog.tsx index 4af658e09e..15adee00ae 100644 --- a/packages/app/src/systems/Overlay/components/OverlayDialog/OverlayDialog.tsx +++ b/packages/app/src/systems/Overlay/components/OverlayDialog/OverlayDialog.tsx @@ -56,7 +56,7 @@ const styles = { '.fuel_Dialog-description': { flex: 1, - overflowY: 'auto', + overflowY: 'auto !important', height: '100%', }, '.fuel_Dialog-heading': { diff --git a/packages/app/src/systems/Transaction/components/TxFee/TxFeeRadio.tsx b/packages/app/src/systems/Transaction/components/TxFee/TxFeeRadio.tsx new file mode 100644 index 0000000000..da2016351f --- /dev/null +++ b/packages/app/src/systems/Transaction/components/TxFee/TxFeeRadio.tsx @@ -0,0 +1,92 @@ +import { Box, Card, HStack, RadioGroupItem, Text } from '@fuel-ui/react'; +import { type BN, DEFAULT_PRECISION } from 'fuels'; +import { type FC, useEffect, useMemo, useState } from 'react'; + +import { cssObj } from '@fuel-ui/css'; +import type { AssetFuelData } from '@fuel-wallet/types'; +import { AssetsCache } from '~/systems/Asset/cache/AssetsCache'; +import { convertToUsd } from '~/systems/Core/utils/convertToUsd'; +import { useProvider } from '~/systems/Network/hooks/useProvider'; +import { formatTip } from '../TxFeeOptions/TxFeeOptions.utils'; +import { TxFee } from './TxFee'; +import { TxFeeLoader } from './TxFeeLoader'; +import { styles } from './styles'; + +export type TxFeeRadioProps = { + fee?: BN; + checked?: boolean; + title: string; +}; + +type TxFeeRadioComponent = FC; + +export const TxFeeRadio: TxFeeRadioComponent = ({ + fee, + checked, + title, +}: TxFeeRadioProps) => { + const [_flag, setFlag] = useState(false); + const provider = useProvider(); + const [baseAsset, setBaseAsset] = useState(); + useEffect(() => { + let abort = false; + const getBaseAsset = async () => { + const [baseAssetId, chainId] = await Promise.all([ + provider?.getBaseAssetId(), + provider?.getChainId(), + ]); + if (abort || baseAssetId == null || chainId == null) return; + const baseAsset = await AssetsCache.getInstance().getAsset({ + chainId: chainId, + assetId: baseAssetId, + dbAssets: [], + save: false, + }); + if (abort) return; + setBaseAsset(baseAsset); + }; + getBaseAsset(); + return () => { + abort = true; + }; + }, [provider]); + + const feeInUsd = useMemo(() => { + if (baseAsset?.rate == null || fee == null) return '$0'; + + return convertToUsd(fee, baseAsset.decimals, baseAsset.rate).formatted; + }, [baseAsset, fee]); + + const ready = !!fee && !!feeInUsd; + + // Horrible workaround to force re-render of this section. + useEffect(() => { + setTimeout(() => { + setFlag((prev) => !prev); + }, 500); + }, [ready]); + + if (!ready) return ; + + return ( + + + + + {fee + ? `${fee.format({ + minPrecision: DEFAULT_PRECISION, + precision: DEFAULT_PRECISION, + })} ETH` + : '--'} + + + ); +}; + +TxFee.Loader = TxFeeLoader; diff --git a/packages/app/src/systems/Transaction/components/TxFee/styles.tsx b/packages/app/src/systems/Transaction/components/TxFee/styles.tsx index aec2e72a0b..9762a9002b 100644 --- a/packages/app/src/systems/Transaction/components/TxFee/styles.tsx +++ b/packages/app/src/systems/Transaction/components/TxFee/styles.tsx @@ -51,4 +51,29 @@ export const styles = { wordWrap: 'break-word', minWidth: 0, }), + option: cssObj({ + alignItems: 'center', + backgroundColor: '$white', + border: '1px solid #e0e0e0', + borderRadius: '10px', + color: '#646464', + cursor: 'pointer', + fontSize: '13px', + gap: '$3', + justifyContent: 'space-between', + padding: '$3', + transition: 'all 0.2s ease', + + '&:hover': { + backgroundColor: '#f0f0f0', + }, + }), + optionContent: cssObj({ + color: '#202020', + }), + optionLabel: cssObj({ + color: '#202020', + fontSize: '13px', + fontWeight: '$medium', + }), }; diff --git a/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx b/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx index f8d160d655..067d924c25 100644 --- a/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx +++ b/packages/app/src/systems/Transaction/components/TxFeeOptions/TxFeeOptions.tsx @@ -1,7 +1,17 @@ import { cssObj } from '@fuel-ui/css'; -import { Box, Button, Form, HStack, Input, Text, VStack } from '@fuel-ui/react'; +import { + Box, + Button, + Form, + HStack, + Input, + RadioGroup, + RadioGroupItem, + Text, + VStack, +} from '@fuel-ui/react'; import { AnimatePresence } from 'framer-motion'; -import { type BN, bn } from 'fuels'; +import { type BN, DEFAULT_PRECISION, bn } from 'fuels'; import { useEffect, useMemo, useRef, useState } from 'react'; import { useController, useFormContext } from 'react-hook-form'; import { MotionFlex, MotionStack, animations } from '~/systems/Core'; @@ -9,6 +19,7 @@ import { createAmount } from '~/systems/Core/components/InputAmount/InputAmount' import { isAmountAllowed } from '~/systems/Core/components/InputAmount/InputAmount.utils'; import type { SendFormValues } from '~/systems/Send/hooks'; import { TxFee } from '../TxFee'; +import { TxFeeRadio } from '../TxFee/TxFeeRadio'; import { DECIMAL_UNITS, formatTip, @@ -178,22 +189,29 @@ export const TxFeeOptions = ({ ) : ( - {options.map((option) => ( - { - previousDefaultTip.current = option.tip; - setValue('fees.tip', { - amount: option.tip, - text: formatTip(option.tip), - }); - onRecalculate?.(option.tip); - }} - /> - ))} + o.tip.eq(tip.value.amount))?.name} + onValueChange={(value) => { + const option = options.find((o) => o.name === value); + if (!option) return; + + previousDefaultTip.current = option.tip; + setValue('fees.tip', { + amount: option.tip, + text: formatTip(option.tip), + }); + onRecalculate?.(option.tip); + }} + > + {options.map((option) => ( + + ))} + )} Date: Tue, 4 Feb 2025 00:41:38 -0300 Subject: [PATCH 85/85] style: adjust transaction operation and group layout spacing --- .../TxDetails/TxOperationsSimple/TxOperationsGroup.tsx | 1 + .../Transaction/components/TxOperation/TxOperation.tsx | 5 ++++- .../Transaction/components/TxOperation/TxOperationCard.tsx | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationsGroup.tsx b/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationsGroup.tsx index f43d3230dd..ba589ca6bf 100644 --- a/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationsGroup.tsx +++ b/packages/app/src/systems/Transaction/components/TxDetails/TxOperationsSimple/TxOperationsGroup.tsx @@ -73,6 +73,7 @@ export function TxOperationsGroup({ const styles = { root: cssObj({ marginTop: '$2', + marginBottom: '$2', backgroundColor: '$gray5', borderRadius: '12px', minHeight: '56px', diff --git a/packages/app/src/systems/Transaction/components/TxOperation/TxOperation.tsx b/packages/app/src/systems/Transaction/components/TxOperation/TxOperation.tsx index 1d6faf0ea6..b957971471 100644 --- a/packages/app/src/systems/Transaction/components/TxOperation/TxOperation.tsx +++ b/packages/app/src/systems/Transaction/components/TxOperation/TxOperation.tsx @@ -84,7 +84,7 @@ export function TxOperation({ // if (isContract && !showNesting && depth !== 0) return null; return ( - + +