diff --git a/frontend/components/SettingsPage/DebugAddresses.tsx b/frontend/components/SettingsPage/DebugAddresses.tsx new file mode 100644 index 00000000..e5bdd7c6 --- /dev/null +++ b/frontend/components/SettingsPage/DebugAddresses.tsx @@ -0,0 +1,233 @@ +import { CopyOutlined } from '@ant-design/icons'; +import { Col, Flex, message, Row, Spin, Tooltip, Typography } from 'antd'; +import { isAddress } from 'ethers/lib/utils'; +import { isEmpty, isNil } from 'lodash'; +import { Fragment, useCallback, useMemo } from 'react'; +import styled from 'styled-components'; + +import { COLOR } from '@/constants/colors'; +import { UNICODE_SYMBOLS } from '@/constants/symbols'; +import { EXPLORER_URL_BY_EVM_CHAIN_ID } from '@/constants/urls'; +import { WalletBalanceResult } from '@/context/BalanceProvider'; +import { EvmChainId, EvmChainName } from '@/enums/Chain'; +import { TokenSymbol } from '@/enums/Token'; +import { WalletType } from '@/enums/Wallet'; +import { + useBalanceContext, + useMasterBalances, +} from '@/hooks/useBalanceContext'; +import { useServices } from '@/hooks/useServices'; +import { useMasterWalletContext } from '@/hooks/useWallet'; +import { Address } from '@/types/Address'; +import { copyToClipboard } from '@/utils/copyToClipboard'; +import { balanceFormat } from '@/utils/numberFormatters'; +import { truncateAddress } from '@/utils/truncate'; + +const { Text, Title } = Typography; + +const Card = styled.div` + padding: 16px 24px; + border-bottom: 1px solid ${COLOR.BORDER_GRAY}; +`; + +const ICON_STYLE = { color: '#606F85' }; + +const getBalanceData = (walletBalances: WalletBalanceResult[]) => { + const result: { [chainId: number]: { [tokenSymbol: string]: number } } = {}; + + for (const walletBalanceResult of walletBalances) { + const { evmChainId: chainId, symbol } = walletBalanceResult; + if (!result[chainId]) result[chainId] = {}; + if (!result[chainId][symbol]) result[chainId][symbol] = 0; + result[chainId][symbol] += walletBalanceResult.balance; + } + + return { balance: result }; +}; + +type DebugItemProps = { + item: { + title: string; + balance: Record>; + address: Address; + link?: { title: string; href: string }; + }; +}; + +const DebugItem = ({ item }: DebugItemProps) => { + const truncatedAddress = truncateAddress(item.address); + + const onCopyToClipboard = useCallback( + () => + copyToClipboard(item.address).then(() => + message.success('Address copied!'), + ), + [item.address], + ); + + const balances = Object.entries(item.balance); + + return ( + + + {item.title} + + {balances.map(([chainId, balance]) => { + const evmChainId = +chainId as keyof typeof EvmChainName; + return ( + + {balances.length > 1 && ( + + + {EvmChainName[evmChainId]}: + + + )} + 1 ? 'mb-16' : undefined}> + + + + Balance{' '} + + + {Object.entries(balance).map(([tokenSymbol, balance]) => { + return ( + + {balanceFormat(balance, 2)} + {tokenSymbol} + + ); + })} + + + + + + + + Address + + + + {truncatedAddress} + + + + + + + + + + ); + })} + {item.link ? ( + + + {item.link.title} {UNICODE_SYMBOLS.EXTERNAL_LINK} + + + ) : null} + + ); +}; + +export const DebugAddresses = () => { + const { masterEoa, masterSafes } = useMasterWalletContext(); + const { serviceWallets: serviceAddresses, selectedAgentConfig } = + useServices(); + const { walletBalances } = useBalanceContext(); + const { masterEoaBalances, masterSafeBalances } = useMasterBalances(); + + const data = useMemo(() => { + if (isNil(masterEoa)) return null; + if (isNil(masterSafes) || isEmpty(masterSafes)) return null; + if (isNil(walletBalances) || isEmpty(walletBalances)) return null; + + const result: { + title: string; + balance: Record< + number | EvmChainId, + Record + >; + address: Address; + link?: { title: string; href: string }; + }[] = []; + + if (!isNil(masterEoaBalances)) { + result.push({ + title: 'Master EOA', + ...getBalanceData(masterEoaBalances), + address: masterEoa.address, + }); + } + + const masterSafe = masterSafes.find( + (item) => item.evmChainId === selectedAgentConfig.evmHomeChainId, + ); + + if (!isNil(masterSafeBalances) && !isNil(masterSafe)) { + result.push({ + title: 'Master Safe', + ...getBalanceData(masterSafeBalances), + address: masterSafe.address, + }); + } + + if (!isNil(serviceAddresses)) { + serviceAddresses?.forEach((serviceWallet) => { + if (!isAddress(serviceWallet.address)) return; + + if (serviceWallet.type === WalletType.EOA) { + result.push({ + title: 'Agent Instance EOA', + ...getBalanceData( + walletBalances.filter( + (balance) => balance.walletAddress === serviceWallet.address, + ), + ), + address: serviceWallet.address, + }); + } + + if (serviceWallet.type === WalletType.Safe) { + result.push({ + title: 'Agent Safe', + ...getBalanceData( + walletBalances.filter( + (walletBalance) => + walletBalance.walletAddress === serviceWallet.address && + walletBalance.evmChainId === serviceWallet.evmChainId, + ), + ), + address: serviceWallet.address, + }); + } + }); + } + + return result; + }, [ + masterEoa, + masterEoaBalances, + masterSafeBalances, + masterSafes, + selectedAgentConfig.evmHomeChainId, + serviceAddresses, + walletBalances, + ]); + + return data ? ( + data.map((item) => ) + ) : ( + + + + ); +}; diff --git a/frontend/components/SettingsPage/DebugInfoSection.tsx b/frontend/components/SettingsPage/DebugInfoSection.tsx index 7d2c9ce8..8c114485 100644 --- a/frontend/components/SettingsPage/DebugInfoSection.tsx +++ b/frontend/components/SettingsPage/DebugInfoSection.tsx @@ -1,245 +1,19 @@ -import { CopyOutlined } from '@ant-design/icons'; -import { - Button, - Col, - Flex, - message, - Row, - Spin, - Tooltip, - Typography, -} from 'antd'; -import { isAddress } from 'ethers/lib/utils'; -import { isEmpty, isNil } from 'lodash'; -import { Fragment, useCallback, useMemo, useState } from 'react'; -import styled from 'styled-components'; +import { Button, Typography } from 'antd'; +import { useCallback, useState } from 'react'; -import { COLOR } from '@/constants/colors'; -import { UNICODE_SYMBOLS } from '@/constants/symbols'; -import { EXPLORER_URL_BY_EVM_CHAIN_ID } from '@/constants/urls'; import { MODAL_WIDTH } from '@/constants/width'; -import { WalletBalanceResult } from '@/context/BalanceProvider'; -import { EvmChainId, EvmChainName } from '@/enums/Chain'; -import { TokenSymbol } from '@/enums/Token'; -import { WalletType } from '@/enums/Wallet'; -import { - useBalanceContext, - useMasterBalances, -} from '@/hooks/useBalanceContext'; -import { useServices } from '@/hooks/useServices'; -import { useMasterWalletContext } from '@/hooks/useWallet'; -import { Address } from '@/types/Address'; -import { copyToClipboard } from '@/utils/copyToClipboard'; -import { balanceFormat } from '@/utils/numberFormatters'; -import { truncateAddress } from '@/utils/truncate'; import { CardSection } from '../styled/CardSection'; import { CustomModal } from '../styled/CustomModal'; +import { DebugAddresses } from './DebugAddresses'; -const { Text, Title } = Typography; - -const Card = styled.div` - padding: 16px 24px; - border-bottom: 1px solid ${COLOR.BORDER_GRAY}; -`; - -const ICON_STYLE = { color: '#606F85' }; - -const getBalanceData = (walletBalances: WalletBalanceResult[]) => { - const result: { [chainId: number]: { [tokenSymbol: string]: number } } = {}; - - for (const walletBalanceResult of walletBalances) { - const { evmChainId: chainId, symbol } = walletBalanceResult; - if (!result[chainId]) result[chainId] = {}; - if (!result[chainId][symbol]) result[chainId][symbol] = 0; - result[chainId][symbol] += walletBalanceResult.balance; - } - - return { balance: result }; -}; - -const DebugItem = ({ - item, -}: { - item: { - title: string; - balance: Record>; - address: Address; - link?: { title: string; href: string }; - }; -}) => { - const truncatedAddress = truncateAddress(item.address); - - const onCopyToClipboard = useCallback( - () => - copyToClipboard(item.address).then(() => - message.success('Address copied!'), - ), - [item.address], - ); - - const balances = Object.entries(item.balance); - - return ( - - - {item.title} - - {balances.map(([chainId, balance]) => { - const evmChainId = +chainId as keyof typeof EvmChainName; - return ( - - {balances.length > 1 && ( - - - {EvmChainName[evmChainId]}: - - - )} - 1 ? 'mb-16' : undefined}> - - - - Balance{' '} - - - {Object.entries(balance).map(([tokenSymbol, balance]) => { - return ( - - {balanceFormat(balance, 2)} - {tokenSymbol} - - ); - })} - - - - - - - - Address - - - - {truncatedAddress} - - - - - - - - - - ); - })} - {item.link ? ( - - - {item.link.title} {UNICODE_SYMBOLS.EXTERNAL_LINK} - - - ) : null} - - ); -}; +const { Text } = Typography; export const DebugInfoSection = () => { - const { masterEoa, masterSafes } = useMasterWalletContext(); - const { serviceWallets: serviceAddresses, selectedAgentConfig } = - useServices(); - const { walletBalances } = useBalanceContext(); - const { masterEoaBalances, masterSafeBalances } = useMasterBalances(); - const [isModalOpen, setIsModalOpen] = useState(false); const showModal = useCallback(() => setIsModalOpen(true), []); const handleCancel = useCallback(() => setIsModalOpen(false), []); - const data = useMemo(() => { - if (isNil(masterEoa)) return null; - if (isNil(masterSafes) || isEmpty(masterSafes)) return null; - if (isNil(walletBalances) || isEmpty(walletBalances)) return null; - - const result: { - title: string; - balance: Record< - number | EvmChainId, - Record - >; - address: Address; - link?: { title: string; href: string }; - }[] = []; - - if (!isNil(masterEoaBalances)) { - result.push({ - title: 'Master EOA', - ...getBalanceData(masterEoaBalances), - address: masterEoa.address, - }); - } - - const masterSafe = masterSafes.find( - (item) => item.evmChainId === selectedAgentConfig.evmHomeChainId, - ); - - if (!isNil(masterSafeBalances) && !isNil(masterSafe)) { - result.push({ - title: 'Master Safe', - ...getBalanceData(masterSafeBalances), - address: masterSafe.address, - }); - } - - if (!isNil(serviceAddresses)) { - serviceAddresses?.forEach((serviceWallet) => { - if (!isAddress(serviceWallet.address)) return; - - if (serviceWallet.type === WalletType.EOA) { - result.push({ - title: 'Agent Instance EOA', - ...getBalanceData( - walletBalances.filter( - (balance) => balance.walletAddress === serviceWallet.address, - ), - ), - address: serviceWallet.address, - }); - } - - if (serviceWallet.type === WalletType.Safe) { - result.push({ - title: 'Agent Safe', - ...getBalanceData( - walletBalances.filter( - (walletBalance) => - walletBalance.walletAddress === serviceWallet.address && - walletBalance.evmChainId === serviceWallet.evmChainId, - ), - ), - address: serviceWallet.address, - }); - } - }); - } - - return result; - }, [ - masterEoa, - masterEoaBalances, - masterSafeBalances, - masterSafes, - selectedAgentConfig.evmHomeChainId, - serviceAddresses, - walletBalances, - ]); - return ( Debug data (for devs) @@ -253,13 +27,7 @@ export const DebugInfoSection = () => { width={MODAL_WIDTH} onCancel={handleCancel} > - {data ? ( - data.map((item) => ) - ) : ( - - - - )} + ); diff --git a/frontend/components/SettingsPage/index.tsx b/frontend/components/SettingsPage/index.tsx index 52847048..06061043 100644 --- a/frontend/components/SettingsPage/index.tsx +++ b/frontend/components/SettingsPage/index.tsx @@ -18,6 +18,7 @@ import { CustomAlert } from '../Alert'; import { CardTitle } from '../Card/CardTitle'; import { CardSection } from '../styled/CardSection'; import { AddBackupWalletPage } from './AddBackupWalletPage'; +import { DebugInfoSection } from './DebugInfoSection'; const { Text, Paragraph } = Typography; @@ -160,6 +161,9 @@ const SettingsMain = () => { Backup wallet {walletBackup} + + {/* Debug info */} + ); };