diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 940eb0181b..6cc768e4aa 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -250,47 +250,13 @@ const useTransactionHistoryWithoutStatuses = (address: Address | undefined) => { const { isSmartContractWallet, isLoading: isLoadingAccountType } = useAccountType() - // Check what type of CCTP (deposit, withdrawal or all) to fetch - // We need this because of Smart Contract Wallets - const cctpTypeToFetch = useCallback( - (chainPair: ChainPair): 'deposits' | 'withdrawals' | 'all' | undefined => { - if (isLoadingAccountType || !chain) { - return undefined - } - if (isSmartContractWallet) { - // fetch based on the connected network - if (chain.id === chainPair.parentChainId) { - return 'deposits' - } - if (chain.id === chainPair.childChainId) { - return 'withdrawals' - } - return undefined - } - // EOA - return isNetwork(chainPair.parentChainId).isTestnet === isTestnetMode - ? 'all' - : undefined - }, - [isSmartContractWallet, isLoadingAccountType, chain, isTestnetMode] - ) - const cctpTransfersMainnet = useCctpFetching({ walletAddress: address, l1ChainId: ChainId.Ethereum, l2ChainId: ChainId.ArbitrumOne, pageNumber: 0, - pageSize: cctpTypeToFetch({ - parentChainId: ChainId.Ethereum, - childChainId: ChainId.ArbitrumOne - }) - ? 1000 - : 0, - type: - cctpTypeToFetch({ - parentChainId: ChainId.Ethereum, - childChainId: ChainId.ArbitrumOne - }) ?? 'all' + pageSize: 1000, + type: 'all' }) const cctpTransfersTestnet = useCctpFetching({ @@ -298,27 +264,20 @@ const useTransactionHistoryWithoutStatuses = (address: Address | undefined) => { l1ChainId: ChainId.Sepolia, l2ChainId: ChainId.ArbitrumSepolia, pageNumber: 0, - pageSize: cctpTypeToFetch({ - parentChainId: ChainId.Sepolia, - childChainId: ChainId.ArbitrumSepolia - }) - ? 1000 - : 0, - type: - cctpTypeToFetch({ - parentChainId: ChainId.Sepolia, - childChainId: ChainId.ArbitrumSepolia - }) ?? 'all' + pageSize: 1000, + type: 'all' }) - // TODO: Clean up this logic when introducing testnet/mainnet split - const combinedCctpTransfers = [ + const combinedCctpMainnetTransfers = [ ...(cctpTransfersMainnet.deposits?.completed || []), ...(cctpTransfersMainnet.withdrawals?.completed || []), + ...(cctpTransfersMainnet.deposits?.pending || []), + ...(cctpTransfersMainnet.withdrawals?.pending || []) + ] + + const combinedCctpTestnetTransfers = [ ...(cctpTransfersTestnet.deposits?.completed || []), ...(cctpTransfersTestnet.withdrawals?.completed || []), - ...(cctpTransfersMainnet.deposits?.pending || []), - ...(cctpTransfersMainnet.withdrawals?.pending || []), ...(cctpTransfersTestnet.deposits?.pending || []), ...(cctpTransfersTestnet.withdrawals?.pending || []) ] @@ -463,7 +422,9 @@ const useTransactionHistoryWithoutStatuses = (address: Address | undefined) => { const transactions = [ ...deposits, ...withdrawals, - ...combinedCctpTransfers + ...(isTestnetMode + ? combinedCctpTestnetTransfers + : combinedCctpMainnetTransfers) ].flat() return { diff --git a/packages/arb-token-bridge-ui/src/state/cctpState.ts b/packages/arb-token-bridge-ui/src/state/cctpState.ts index 7f0eb43f84..bfc64a85d3 100644 --- a/packages/arb-token-bridge-ui/src/state/cctpState.ts +++ b/packages/arb-token-bridge-ui/src/state/cctpState.ts @@ -1,8 +1,9 @@ -import { BigNumber } from 'ethers' import { useCallback, useEffect, useMemo, useState } from 'react' import { create } from 'zustand' import useSWRImmutable from 'swr/immutable' import { useInterval } from 'react-use' +import { useAccount, useChainId, useSigner } from 'wagmi' +import dayjs from 'dayjs' import { getCctpUtils } from '@/token-bridge-sdk/cctp' import { @@ -14,8 +15,6 @@ import { import { fetchCCTPDeposits, fetchCCTPWithdrawals } from '../util/cctp/fetchCCTP' import { DepositStatus, MergedTransaction, WithdrawalStatus } from './app/state' import { normalizeTimestamp } from './app/utils' -import { useAccount, useSigner } from 'wagmi' -import dayjs from 'dayjs' import { ChainDomain, CompletedCCTPTransfer, @@ -173,6 +172,7 @@ type fetchCctpParams = { pageSize: number enabled: boolean } + export const useCCTPDeposits = ({ walletAddress, l1ChainId, @@ -180,6 +180,10 @@ export const useCCTPDeposits = ({ pageSize, enabled }: fetchCctpParams) => { + const { isSmartContractWallet } = useAccountType() + const chainId = useChainId() + const { isEthereumMainnetOrTestnet } = isNetwork(chainId) + return useSWRImmutable( // Only fetch when we have walletAddress () => { @@ -194,7 +198,9 @@ export const useCCTPDeposits = ({ walletAddress: _walletAddress, l1ChainId: _l1ChainId, pageNumber: _pageNumber, - pageSize: _pageSize + pageSize: _pageSize, + connectedToEthereum: isEthereumMainnetOrTestnet, + isSmartContractWallet }) .then(deposits => parseSWRResponse(deposits, _l1ChainId)) .then(deposits => { @@ -218,6 +224,10 @@ export const useCCTPWithdrawals = ({ pageSize, enabled }: fetchCctpParams) => { + const { isSmartContractWallet } = useAccountType() + const chainId = useChainId() + const { isEthereumMainnetOrTestnet } = isNetwork(chainId) + return useSWRImmutable( // Only fetch when we have walletAddress () => { @@ -238,7 +248,9 @@ export const useCCTPWithdrawals = ({ walletAddress: _walletAddress, l1ChainId: _l1ChainId, pageNumber: _pageNumber, - pageSize: _pageSize + pageSize: _pageSize, + connectedToEthereum: isEthereumMainnetOrTestnet, + isSmartContractWallet }) .then(withdrawals => parseSWRResponse(withdrawals, _l1ChainId)) .then(withdrawals => { diff --git a/packages/arb-token-bridge-ui/src/util/cctp/fetchCCTP.ts b/packages/arb-token-bridge-ui/src/util/cctp/fetchCCTP.ts index bddf51db87..249ee44d73 100644 --- a/packages/arb-token-bridge-ui/src/util/cctp/fetchCCTP.ts +++ b/packages/arb-token-bridge-ui/src/util/cctp/fetchCCTP.ts @@ -12,6 +12,8 @@ export type FetchParams = { l1ChainId: ChainId pageNumber: number pageSize: number + connectedToEthereum: boolean + isSmartContractWallet: boolean } function convertStringToUsdcBigNumber(amount: string) { @@ -26,12 +28,48 @@ function mapCCTPTransfer( return cctpTransfer } +function sanitizeSmartContractWalletCctpTransfers< + T extends PendingCCTPTransfer | CompletedCCTPTransfer +>({ + type, + walletAddress, + transfers, + connectedToEthereum +}: { + type: 'deposits' | 'withdrawals' + walletAddress: string + transfers: T[] + connectedToEthereum: boolean +}): T[] { + const walletAddressLowercased = walletAddress.toLowerCase() + + return transfers.filter(tx => { + const { sender, recipient } = tx.messageSent + const senderLowercased = sender.toLowerCase() + const recipientLowercased = recipient.toLowerCase() + + if (type === 'deposits') { + if (connectedToEthereum) { + return senderLowercased === walletAddressLowercased + } + return recipientLowercased === walletAddressLowercased + } + + if (connectedToEthereum) { + return recipientLowercased === walletAddressLowercased + } + return senderLowercased === walletAddressLowercased + }) as T[] +} + async function fetchCCTP({ walletAddress, l1ChainId, pageNumber, pageSize, - type + type, + connectedToEthereum, + isSmartContractWallet }: FetchParams & { type: 'deposits' | 'withdrawals' }): Promise< Response['data'] > { @@ -40,7 +78,9 @@ async function fetchCCTP({ walletAddress, l1ChainId, pageNumber, - pageSize + pageSize, + connectedToEthereum, + isSmartContractWallet }) ) @@ -57,9 +97,31 @@ async function fetchCCTP({ const parsedResponse: Response = await response.json() const { pending, completed } = parsedResponse.data + const sanitizedPendingTransfers = isSmartContractWallet + ? sanitizeSmartContractWalletCctpTransfers({ + type, + walletAddress, + transfers: pending, + connectedToEthereum + }) + : pending + + const sanitizedCompletedTransfers = isSmartContractWallet + ? sanitizeSmartContractWalletCctpTransfers({ + type, + walletAddress, + transfers: completed, + connectedToEthereum + }) + : completed + return { - pending: pending.map(transfer => mapCCTPTransfer(transfer)), - completed: completed.map(transfer => mapCCTPTransfer(transfer)) + pending: sanitizedPendingTransfers.map(transfer => + mapCCTPTransfer(transfer) + ), + completed: sanitizedCompletedTransfers.map(transfer => + mapCCTPTransfer(transfer) + ) } }