From be0e9e0c60358ad0598923922703a3cdc6e0e9fc Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 26 Oct 2023 18:54:49 +0200 Subject: [PATCH 01/72] cctp --- .../TransactionsTableClaimableRow.tsx | 5 ++ .../useCompleteMultiChainTransactions.ts | 53 +++++++++++++--- .../src/state/cctpState.ts | 61 ++++++++++++++----- 3 files changed, 94 insertions(+), 25 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/NewTransactionHistory/TransactionsTableClaimableRow.tsx b/packages/arb-token-bridge-ui/src/components/NewTransactionHistory/TransactionsTableClaimableRow.tsx index f1e57c2c8a..4c0d841195 100644 --- a/packages/arb-token-bridge-ui/src/components/NewTransactionHistory/TransactionsTableClaimableRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/NewTransactionHistory/TransactionsTableClaimableRow.tsx @@ -38,10 +38,15 @@ type CommonProps = { function ClaimableRowStatus({ tx }: CommonProps) { const { parentLayer, layer } = useChainLayers() + const { isConfirmed } = useRemainingTime(tx) const matchingL1Tx = tx.isCctp ? tx.cctpData?.receiveMessageTransactionHash : findMatchingL1TxForWithdrawal(tx) + if (tx.isCctp && isConfirmed) { + tx.status = 'Confirmed' + } + switch (tx.status) { case 'pending': return ( diff --git a/packages/arb-token-bridge-ui/src/hooks/useCompleteMultiChainTransactions.ts b/packages/arb-token-bridge-ui/src/hooks/useCompleteMultiChainTransactions.ts index 0d5e5642da..26fed18738 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useCompleteMultiChainTransactions.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useCompleteMultiChainTransactions.ts @@ -28,6 +28,7 @@ import { } from '../util/withdrawals/helpers' import { FetchWithdrawalsFromSubgraphResult } from '../util/withdrawals/fetchWithdrawalsFromSubgraph' import { updateAdditionalDepositData } from '../util/deposits/helpers' +import { useCctpFetching } from '../state/cctpState' const PAGE_SIZE = 100 @@ -48,8 +49,13 @@ export type Withdrawal = ( AdditionalProperties type DepositOrWithdrawal = Deposit | Withdrawal +type Transfer = DepositOrWithdrawal | MergedTransaction + +function getStandardizedTimestampByTx(tx: Transfer) { + if (isCctpTransfer(tx)) { + return (tx.createdAt ?? 0) / 1_000 + } -function getStandardizedTimestampByTx(tx: DepositOrWithdrawal) { if (isDeposit(tx)) { return tx.timestampCreated ?? 0 } @@ -61,10 +67,7 @@ function getStandardizedTimestampByTx(tx: DepositOrWithdrawal) { return getStandardizedTimestamp(tx.timestamp ?? '0') } -function sortByTimestampDescending( - a: DepositOrWithdrawal, - b: DepositOrWithdrawal -) { +function sortByTimestampDescending(a: Transfer, b: Transfer) { return getStandardizedTimestampByTx(a) > getStandardizedTimestampByTx(b) ? -1 : 1 @@ -105,6 +108,12 @@ function isWithdrawalFromSubgraph( return tx.source === 'subgraph' } +function isCctpTransfer( + tx: DepositOrWithdrawal | MergedTransaction +): tx is MergedTransaction { + return !!(tx as MergedTransaction).isCctp +} + function isDeposit(tx: DepositOrWithdrawal): tx is Deposit { return tx.direction === 'deposit' } @@ -165,7 +174,7 @@ function getProvider(chainId: ChainId) { const useTransactionListByDirection = ( direction: 'deposits' | 'withdrawals' ) => { - const [transactions, setTransactions] = useState([]) + const [transactions, setTransactions] = useState([]) const [page, setPage] = useState(0) const [loading, setLoading] = useState(true) @@ -174,6 +183,25 @@ const useTransactionListByDirection = ( const { address } = useAccount() + const cctpTransfers = useCctpFetching({ + walletAddress: address, + l1ChainId: ChainId.Goerli, + l2ChainId: ChainId.ArbitrumGoerli, + pageNumber: 0, + pageSize: 1000, + type: direction + }) + + const combinedCctpTransfers = [ + ...(cctpTransfers[direction]?.completed || []), + ...(cctpTransfers[direction]?.pending || []) + ] + + const cctpLoading = + direction === 'deposits' + ? cctpTransfers.isLoadingDeposits + : cctpTransfers.isLoadingWithdrawals + const shouldFetchPage = useCallback( (index: number) => { const _page = latestPage.current @@ -242,8 +270,10 @@ const useTransactionListByDirection = ( }, [data, shouldFetchPage]) return { - data: transactions.flat(), - loading, + data: [...transactions.flat(), ...combinedCctpTransfers].sort( + sortByTimestampDescending + ), + loading: loading || cctpLoading, error } } @@ -297,7 +327,12 @@ export const useCompleteMultiChainTransactions = () => { const endIndex = startIndex + MAX_BATCH_SIZE return Promise.all( - data.slice(startIndex, endIndex).map(transformTransaction) + data.slice(startIndex, endIndex).map(tx => { + if (isCctpTransfer(tx)) { + return tx + } + return transformTransaction(tx) + }) ) } ) diff --git a/packages/arb-token-bridge-ui/src/state/cctpState.ts b/packages/arb-token-bridge-ui/src/state/cctpState.ts index c5d927c540..ea0ae844f9 100644 --- a/packages/arb-token-bridge-ui/src/state/cctpState.ts +++ b/packages/arb-token-bridge-ui/src/state/cctpState.ts @@ -115,9 +115,8 @@ function parseTransferToMergedTransaction( tokenAddress: getUsdcTokenAddressFromSourceChainId(sourceChainId), depositStatus: DepositStatus.CCTP_DEFAULT_STATE, isCctp: true, - // TODO: Fix those when adding CCTP - parentChainId: 0, - chainId: 0, + parentChainId: isDeposit ? sourceChainId : chainId, + chainId: isDeposit ? chainId : sourceChainId, cctpData: { sourceChainId, attestationHash: messageSent.attestationHash, @@ -180,10 +179,22 @@ export const useCCTPDeposits = ({ l1ChainId: _l1ChainId, pageNumber: _pageNumber, pageSize: _pageSize - }).then(deposits => parseSWRResponse(deposits, _l1ChainId)) + }) + .then(deposits => parseSWRResponse(deposits, _l1ChainId)) + .then(deposits => { + return { + completed: deposits.completed, + pending: deposits.pending.map(tx => { + return { + ...tx, + status: isTransferConfirmed(tx) ? 'Confirmed' : tx.status + } + }) + } + }) ) - return { data, error, isLoading } + return { data, error, isLoading: isLoading } } export const useCCTPWithdrawals = ({ @@ -214,7 +225,19 @@ export const useCCTPWithdrawals = ({ l1ChainId: _l1ChainId, pageNumber: _pageNumber, pageSize: _pageSize - }).then(withdrawals => parseSWRResponse(withdrawals, _l1ChainId)) + }) + .then(withdrawals => parseSWRResponse(withdrawals, _l1ChainId)) + .then(withdrawals => { + return { + completed: withdrawals.completed, + pending: withdrawals.pending.map(tx => { + return { + ...tx, + status: isTransferConfirmed(tx) ? 'Confirmed' : tx.status + } + }) + } + }) ) return { data, error, isLoading, isValidating } @@ -616,13 +639,24 @@ export function getTargetChainIdFromSourceChain(tx: MergedTransaction) { }[tx.cctpData.sourceChainId] } -export function useRemainingTime(tx: MergedTransaction) { +function getConfirmedDate(tx: MergedTransaction) { const l1SourceChain = getL1ChainIdFromSourceChain(tx) const requiredL1BlocksBeforeConfirmation = getBlockBeforeConfirmation(l1SourceChain) const blockTime = getBlockTime(l1SourceChain) - const [remainingTime, setRemainingTime] = useState('Calculating...') + return dayjs(tx.createdAt).add( + requiredL1BlocksBeforeConfirmation * blockTime, + 'seconds' + ) +} + +function isTransferConfirmed(tx: MergedTransaction) { + return dayjs().isAfter(getConfirmedDate(tx)) +} + +export function useRemainingTime(tx: MergedTransaction) { + const [remainingTime, setRemainingTime] = useState() const [canBeClaimedDate, setCanBeClaimedDate] = useState() const [isConfirmed, setIsConfirmed] = useState( tx.status === 'Confirmed' || tx.status === 'Executed' @@ -639,20 +673,15 @@ export function useRemainingTime(tx: MergedTransaction) { return } - setCanBeClaimedDate( - dayjs(tx.createdAt).add( - requiredL1BlocksBeforeConfirmation * blockTime, - 'seconds' - ) - ) - }, [blockTime, requiredL1BlocksBeforeConfirmation, tx.createdAt, tx.status]) + setCanBeClaimedDate(getConfirmedDate(tx)) + }, [tx]) useInterval(() => { if (!canBeClaimedDate) { return } - if (dayjs().isAfter(canBeClaimedDate)) { + if (isTransferConfirmed(tx)) { setIsConfirmed(true) } else { setRemainingTime(canBeClaimedDate.fromNow().toString()) From 188be7089439d17d813e83109b295ef614390ced Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 31 Oct 2023 19:59:10 +0100 Subject: [PATCH 02/72] infinite scrolling --- .../components/MainContent/MainContent.tsx | 17 +- .../NewTransactionHistory.tsx | 30 ++- .../TransactionHistoryTable.tsx | 181 +++++++++++------- .../useCompleteMultiChainTransactions.ts | 65 +++++-- 4 files changed, 202 insertions(+), 91 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx index 6c79893075..b37ce7d408 100644 --- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx +++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx @@ -7,6 +7,7 @@ import { useAppContextActions, useAppContextState } from '../App/AppContext' import { ArbitrumStats, statsLocalStorageKey } from './ArbitrumStats' import { SettingsDialog } from '../common/SettingsDialog' import { NewTransactionHistory } from '../NewTransactionHistory/NewTransactionHistory' +import { useCompleteMultiChainTransactions } from '../../hooks/useCompleteMultiChainTransactions' export const motionDivProps = { layout: true, @@ -33,6 +34,14 @@ export function MainContent() { const [isArbitrumStatsVisible] = useLocalStorage(statsLocalStorageKey) + const { + data: { transactions }, + loading, + completed, + error, + resume + } = useCompleteMultiChainTransactions() + return (
@@ -53,7 +62,13 @@ export function MainContent() { isOpen={isTransactionHistoryPanelVisible} onClose={closeTransactionHistoryPanel} > - + {/* Settings panel */} diff --git a/packages/arb-token-bridge-ui/src/components/NewTransactionHistory/NewTransactionHistory.tsx b/packages/arb-token-bridge-ui/src/components/NewTransactionHistory/NewTransactionHistory.tsx index a242b12631..4e819e671e 100644 --- a/packages/arb-token-bridge-ui/src/components/NewTransactionHistory/NewTransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/NewTransactionHistory/NewTransactionHistory.tsx @@ -1,4 +1,4 @@ -import { useMemo } from 'react' +import { useMemo, useRef } from 'react' import { Tab } from '@headlessui/react' import { twMerge } from 'tailwind-merge' import { useAccount } from 'wagmi' @@ -18,16 +18,22 @@ import { TabButton } from '../common/Tab' const roundedTabClasses = 'roundedTab ui-not-selected:arb-hover relative flex flex-row flex-nowrap items-center gap-0.5 md:gap-2 rounded-tl-lg rounded-tr-lg px-2 md:px-4 py-2 text-base ui-selected:bg-white ui-not-selected:text-white justify-center md:justify-start grow md:grow-0' -export const NewTransactionHistory = () => { +export const NewTransactionHistory = ({ + transactions, + loading, + completed, + error, + resume +}: { + transactions: MergedTransaction[] + loading: boolean + completed: boolean + error: unknown + resume: () => void +}) => { const { address } = useAccount() - const { - data: { transactions }, - loading, - completed, - paused, - error - } = useCompleteMultiChainTransactions() + const tabPanelsRef = useRef(null) const groupedTransactions = useMemo( () => @@ -103,14 +109,18 @@ export const NewTransactionHistory = () => { transactions={pendingTransactions} className="rounded-tl-none" loading={loading} + completed={completed} error={error} + resume={resume} /> - + diff --git a/packages/arb-token-bridge-ui/src/components/NewTransactionHistory/TransactionHistoryTable.tsx b/packages/arb-token-bridge-ui/src/components/NewTransactionHistory/TransactionHistoryTable.tsx index 8ec2f652ec..74e4413829 100644 --- a/packages/arb-token-bridge-ui/src/components/NewTransactionHistory/TransactionHistoryTable.tsx +++ b/packages/arb-token-bridge-ui/src/components/NewTransactionHistory/TransactionHistoryTable.tsx @@ -1,4 +1,4 @@ -import { PropsWithChildren } from 'react' +import { PropsWithChildren, useEffect, useRef } from 'react' import { twMerge } from 'tailwind-merge' import { MergedTransaction } from '../../state/app/state' @@ -10,6 +10,7 @@ import { SafeImage } from '../common/SafeImage' import { Loader } from '../common/atoms/Loader' import { ExternalLink } from '../common/ExternalLink' import { GET_HELP_LINK } from '../../constants' +import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline' export const TransactionDateTime = ({ standardizedDate @@ -75,78 +76,130 @@ export const TransactionHistoryTable = ({ transactions, className, loading, - error + completed, + error, + resume }: { transactions: MergedTransaction[] className?: string loading: boolean + completed: boolean error: unknown + resume: () => void }) => { + const paused = !loading && !completed + + const tabPanelsRef = useRef(null) + + function isScrollable() { + const el = tabPanelsRef.current + return el && el.scrollHeight > el.clientHeight + } + + function isScrolledToBottom() { + const el = tabPanelsRef.current + + if (!el || !isScrollable()) { + return false + } + + const scrollPosition = el.scrollTop + el.clientHeight + return scrollPosition >= el.scrollHeight + } + + function handleScroll() { + if (paused && isScrolledToBottom()) { + resume() + } + } + return ( -
-
- - - Status - Date - Token - Networks - - - - {transactions.length === 0 ? ( - error ? ( -
- - We seem to be having a difficult time loading your data. - Please give it a moment and then try refreshing the page. If - the problem persists please file a ticket{' '} - - here - - . - -
- ) : ( -
- - {loading ? ( -
- - Loading... -
- ) : ( - 'Looks like no transactions here yet!' - )} -
-
- ) - ) : ( - transactions.map(tx => - tx.isWithdrawal ? ( - + <> +
+
+
console.log('1')} + > + + Status + Date + Token + Networks + + + console.log('2')} + > + {transactions.length === 0 ? ( + error ? ( +
+ + We seem to be having a difficult time loading your data. + Please give it a moment and then try refreshing the page. + If the problem persists please file a ticket{' '} + + here + + . + +
) : ( - +
+ + {loading ? ( +
+ + Loading... +
+ ) : ( + 'Looks like no transactions here yet!' + )} +
+
) - ) - )} -
-
+ ) : ( + transactions.map(tx => + tx.isWithdrawal ? ( + + ) : ( + + ) + ) + )} + + +
-
+ {paused && !isScrollable() && ( +
+ +
+ )} + ) } diff --git a/packages/arb-token-bridge-ui/src/hooks/useCompleteMultiChainTransactions.ts b/packages/arb-token-bridge-ui/src/hooks/useCompleteMultiChainTransactions.ts index c8561c6ccd..7acee386f0 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useCompleteMultiChainTransactions.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useCompleteMultiChainTransactions.ts @@ -29,8 +29,6 @@ import { FetchWithdrawalsFromSubgraphResult } from '../util/withdrawals/fetchWit import { updateAdditionalDepositData } from '../util/deposits/helpers' import { useCctpFetching } from '../state/cctpState' -const PAGE_SIZE = 100 - export type AdditionalTransferProperties = { direction: 'deposit' | 'withdrawal' source: 'subgraph' | 'event_logs' @@ -173,6 +171,8 @@ function getProvider(chainId: ChainId) { const useTransactionListByDirection = ( direction: 'deposits' | 'withdrawals' ) => { + const PAGE_SIZE = 100 + const [transactions, setTransactions] = useState([]) const [page, setPage] = useState(0) const [loading, setLoading] = useState(true) @@ -298,22 +298,32 @@ const useMultiChainTransactionList = () => { * Maps additional info to previously fetches transaction history, starting with the earliest data. * This is done in small batches to safely meet RPC limits. */ -export const useCompleteMultiChainTransactions = () => { +export const useCompleteMultiChainTransactions = (): { + data: { + transactions: MergedTransaction[] + total: number | undefined + } + loading: boolean + completed: boolean + error: unknown + pause: () => void + resume: () => void +} => { // max number of transactions mapped in parallel, for the same chain pair // we can batch more than MAX_BATCH_SIZE at a time if they use a different RPC // MAX_BATCH_SIZE means max number of transactions in a batch for a chain pair const MAX_BATCH_SIZE = 5 + const PAUSE_SIZE = 20 const [transactions, setTransactions] = useState([]) const [page, setPage] = useState(0) const [fetching, setFetching] = useState(true) - const [paused, setPaused] = useState(false) const { data, loading, error } = useMultiChainTransactionList() const { address } = useAccount() const { data: mapData, error: mapError } = useSWRImmutable( - address && !loading ? [address, page] : null, + address && !loading && fetching ? [address, page] : null, ([, _page]) => { // TODO: Need to allow more than MAX_BATCH_SIZE if they are for diff chain pairs // MAX_BATCH_SIZE refers to the same chain pair only @@ -331,16 +341,32 @@ export const useCompleteMultiChainTransactions = () => { } ) + function pause() { + setFetching(false) + } + + function resume() { + setFetching(true) + setPage(prevPage => prevPage + 1) + } + useEffect(() => { if (mapData) { - setTransactions(prevTransactions => [ - ...prevTransactions, - ...(mapData.filter(Boolean) as MergedTransaction[]) - ]) + setTransactions(prevTransactions => { + const newTransactions = mapData.filter(Boolean) as MergedTransaction[] + return [...prevTransactions, ...newTransactions] + }) + // mapped data is a full page, so we need to fetch more pages if (mapData.length === MAX_BATCH_SIZE) { - // full batch size has been mapped, which means there may be more txs to map - setPage(prevPage => prevPage + 1) + setPage(prevPage => { + // we fetched enough transactions to pause + if (MAX_BATCH_SIZE * (prevPage + 1) >= PAUSE_SIZE) { + pause() + return prevPage + } + return prevPage + 1 + }) } else { setFetching(false) } @@ -349,17 +375,24 @@ export const useCompleteMultiChainTransactions = () => { if (loading || error) { return { - data: { transactions: [] as MergedTransaction[], total: undefined }, + data: { + transactions: [] as MergedTransaction[], + total: undefined + }, + completed: false, loading, - error + error, + pause, + resume } } return { data: { transactions, total: data.length }, loading: fetching, - completed: !fetching && !paused, - paused, - error: mapError ?? error + completed: transactions.length === data.length, + error: mapError ?? error, + pause, + resume } } From 67f4c5de7f848c1d9a4997926c10bf9fca4615df Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 4 Dec 2023 12:21:08 +0100 Subject: [PATCH 03/72] fixes --- .../src/hooks/useTransactionHistory.ts | 10 +++++++--- packages/arb-token-bridge-ui/src/state/app/state.ts | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index d1c7ff6bcf..8f8aec310d 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -108,11 +108,15 @@ function isDeposit(tx: DepositOrWithdrawal): tx is Deposit { } async function transformTransaction( - tx: DepositOrWithdrawal + tx: DepositOrWithdrawal | MergedTransaction ): Promise { const parentChainProvider = getProvider(tx.parentChainId) const childChainProvider = getProvider(tx.childChainId) + if (isCctpTransfer(tx)) { + return tx + } + if (isDeposit(tx)) { return transformDeposit( await updateAdditionalDepositData({ @@ -313,13 +317,13 @@ const useTransactionHistoryWithoutStatuses = ( }, [withdrawalsData]) // merge deposits and withdrawals and sort them by date - const transactions = [...deposits, ...withdrawals] + const transactions = [...deposits, ...withdrawals, ...combinedCctpTransfers] .flat() .sort(sortByTimestampDescending) return { data: transactions, - loading: depositsLoading || withdrawalsLoading, + loading: depositsLoading || withdrawalsLoading || cctpLoading, error: depositsError ?? withdrawalsError } } diff --git a/packages/arb-token-bridge-ui/src/state/app/state.ts b/packages/arb-token-bridge-ui/src/state/app/state.ts index b5dc352b82..ede19fdb54 100644 --- a/packages/arb-token-bridge-ui/src/state/app/state.ts +++ b/packages/arb-token-bridge-ui/src/state/app/state.ts @@ -62,7 +62,7 @@ export interface MergedTransaction { sender?: string destination?: string direction: TxnType - status?: string // TODO: Use enums + status: string | undefined // TODO: Use enums createdAt: number | null resolvedAt: number | null txId: string From 322845255dbe8d4c46fa77cdc722ee0b6498ef12 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 5 Dec 2023 17:13:37 +0100 Subject: [PATCH 04/72] clean up --- .../src/components/TransactionHistory/TransactionHistory.tsx | 2 +- .../components/TransactionHistory/TransactionHistoryTable.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx index 498c7ceb73..d52dad55b5 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx @@ -1,4 +1,4 @@ -import { useMemo, useRef } from 'react' +import { useMemo } from 'react' import { Tab } from '@headlessui/react' import { twMerge } from 'tailwind-merge' import { useAccount } from 'wagmi' diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx index 87acd7e4d6..fbc3c1546f 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx @@ -8,7 +8,6 @@ import { Table } from 'react-virtualized' import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline' -import {} from 'react' import { MergedTransaction } from '../../state/app/state' import { From 43fb44eacf8097b91c1e4519c9d2a9d93d5a1dae Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 5 Dec 2023 17:55:08 +0100 Subject: [PATCH 05/72] fixes --- .../TransactionHistoryTable.tsx | 194 ++++++++++-------- 1 file changed, 110 insertions(+), 84 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx index fbc3c1546f..98e9a60e23 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx @@ -114,10 +114,17 @@ export const TransactionHistoryTable = ({ } function handleScroll(e: ScrollParams | ScrollEventData) { + console.log( + `Result for ${transactions[0]?.status}: ${ + e.scrollHeight > e.clientHeight + }` + ) if (paused && isScrolledToBottom(e)) { resume() } - setScrollable(e.scrollHeight > e.clientHeight) + console.log('scrollH: ', e.scrollHeight) + console.log('clientH: ', e.clientHeight) + setScrollable(e.clientHeight > 0 && e.scrollHeight > e.clientHeight) } const getRowHeight = useCallback( @@ -135,6 +142,8 @@ export const TransactionHistoryTable = ({ [transactions, rowHeight, rowHeightCustomDestinationAddress] ) + console.log({ scrollable }) + if (isTxHistoryEmpty) { if (error) { return ( @@ -162,6 +171,25 @@ export const TransactionHistoryTable = ({
) } + if (paused) { + return ( +
+
+ + We could not find any recent transactions! + +
+
+ +
+
+ ) + } return (
Looks like no transactions here yet! @@ -170,91 +198,89 @@ export const TransactionHistoryTable = ({ } return ( - <> -
- - {({ width, height }) => ( - getRowHeight(index)} - rowCount={transactions.length} - headerHeight={52} - headerRowRenderer={props => ( -
- {props.columns} -
- )} - className="table-auto" - rowGetter={({ index }) => transactions[index]} - rowRenderer={({ index, key, style }) => { - const tx = transactions[index] - const isEvenRow = index % 2 === 0 +
+ + {({ width, height }) => ( +
getRowHeight(index)} + rowCount={transactions.length} + headerHeight={52} + headerRowRenderer={props => ( +
+ {props.columns} +
+ )} + className="table-auto" + rowGetter={({ index }) => transactions[index]} + rowRenderer={({ index, key, style }) => { + const tx = transactions[index] + const isEvenRow = index % 2 === 0 - if (!tx) { - return null - } + if (!tx) { + return null + } - return ( -
- {tx.isWithdrawal ? ( - - ) : ( - - )} -
- ) - }} - > - {/* TODO: FIX LAYOUT FOR HEADERS AND COLUMNS: WIDTH AND PADDING */} - ( - Status - )} - /> - ( - Date - )} - /> - ( - Token - )} - /> - ( - Networks - )} - /> -
- )} -
-
+ return ( +
+ {tx.isWithdrawal ? ( + + ) : ( + + )} +
+ ) + }} + > + {/* TODO: FIX LAYOUT FOR HEADERS AND COLUMNS: WIDTH AND PADDING */} + ( + Status + )} + /> + ( + Date + )} + /> + ( + Token + )} + /> + ( + Networks + )} + /> + + )} + {paused && !scrollable && ( -
+
)} - +
) } From 6c4679040a4f29b4d2fb540fdb94bc8581bc4d46 Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 6 Dec 2023 12:06:58 +0100 Subject: [PATCH 06/72] remove logs --- .../TransactionHistory/TransactionHistoryTable.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx index 98e9a60e23..d771a57828 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx @@ -114,16 +114,9 @@ export const TransactionHistoryTable = ({ } function handleScroll(e: ScrollParams | ScrollEventData) { - console.log( - `Result for ${transactions[0]?.status}: ${ - e.scrollHeight > e.clientHeight - }` - ) if (paused && isScrolledToBottom(e)) { resume() } - console.log('scrollH: ', e.scrollHeight) - console.log('clientH: ', e.clientHeight) setScrollable(e.clientHeight > 0 && e.scrollHeight > e.clientHeight) } @@ -142,8 +135,6 @@ export const TransactionHistoryTable = ({ [transactions, rowHeight, rowHeightCustomDestinationAddress] ) - console.log({ scrollable }) - if (isTxHistoryEmpty) { if (error) { return ( From 2f72b6ce7417e48b97cdbf22f8a16429ade3b221 Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 6 Dec 2023 14:27:22 +0100 Subject: [PATCH 07/72] fixes --- .../TransactionHistoryTable.tsx | 2 +- .../TransactionsTableClaimableRow.tsx | 5 ----- .../components/TransactionHistory/helpers.ts | 4 ++++ .../src/hooks/useTransactionHistory.ts | 21 ++++++++++--------- .../src/state/cctpState.ts | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx index 856ddc0b93..15e30710e7 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx @@ -172,7 +172,7 @@ export const TransactionHistoryTable = ({ key={key} style={{ ...style, height: `${getRowHeight(index)}px` }} > - {tx.isWithdrawal ? ( + {tx.isWithdrawal || tx.isCctp ? ( { +export function isCctpTransfer(tx: Transfer): tx is MergedTransaction { + return !!(tx as MergedTransaction).isCctp +} + +async function transformTransaction(tx: Transfer): Promise { const parentChainProvider = getProvider(tx.parentChainId) const childChainProvider = getProvider(tx.childChainId) @@ -200,11 +196,16 @@ const useTransactionHistoryWithoutStatuses = ( type: 'all' }) + // TODO: Clean up this logic when introducing testnet/mainnet split const combinedCctpTransfers = [ ...(cctpTransfersMainnet.deposits?.completed || []), ...(cctpTransfersMainnet.withdrawals?.completed || []), ...(cctpTransfersTestnet.deposits?.completed || []), - ...(cctpTransfersTestnet.withdrawals?.completed || []) + ...(cctpTransfersTestnet.withdrawals?.completed || []), + ...(cctpTransfersMainnet.deposits?.pending || []), + ...(cctpTransfersMainnet.withdrawals?.pending || []), + ...(cctpTransfersTestnet.deposits?.pending || []), + ...(cctpTransfersTestnet.withdrawals?.pending || []) ] const cctpLoading = diff --git a/packages/arb-token-bridge-ui/src/state/cctpState.ts b/packages/arb-token-bridge-ui/src/state/cctpState.ts index a025eb9bb0..9025a79b61 100644 --- a/packages/arb-token-bridge-ui/src/state/cctpState.ts +++ b/packages/arb-token-bridge-ui/src/state/cctpState.ts @@ -687,7 +687,7 @@ function getConfirmedDate(tx: MergedTransaction) { ) } -function isTransferConfirmed(tx: MergedTransaction) { +export function isTransferConfirmed(tx: MergedTransaction) { return dayjs().isAfter(getConfirmedDate(tx)) } From f2dc99e54e8b242571f18b55ddfe936a74d0d1d0 Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 6 Dec 2023 14:50:28 +0100 Subject: [PATCH 08/72] fix --- .../src/state/cctpState.ts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/state/cctpState.ts b/packages/arb-token-bridge-ui/src/state/cctpState.ts index 9025a79b61..ff0965e16b 100644 --- a/packages/arb-token-bridge-ui/src/state/cctpState.ts +++ b/packages/arb-token-bridge-ui/src/state/cctpState.ts @@ -59,6 +59,21 @@ function getSourceChainIdFromSourceDomain( return isTestnet ? ChainId.ArbitrumGoerli : ChainId.ArbitrumOne } +function getDestinationChainIdFromSourceDomain( + sourceDomain: ChainDomain, + chainId: ChainId +): CCTPSupportedChainId { + const { isTestnet } = isNetwork(chainId) + + // Deposits + if (sourceDomain === ChainDomain.Ethereum) { + return isTestnet ? ChainId.ArbitrumGoerli : ChainId.ArbitrumOne + } + + // Withdrawals + return isTestnet ? ChainId.Goerli : ChainId.Ethereum +} + export function getUSDCAddresses(chainId: CCTPSupportedChainId) { return { [ChainId.Ethereum]: CommonAddress.Ethereum, @@ -95,6 +110,10 @@ function parseTransferToMergedTransaction( parseInt(messageSent.sourceDomain, 10), chainId ) + const destinationChainId = getDestinationChainIdFromSourceDomain( + parseInt(messageSent.sourceDomain, 10), + chainId + ) const isDeposit = parseInt(messageSent.sourceDomain, 10) === ChainDomain.Ethereum @@ -118,7 +137,7 @@ function parseTransferToMergedTransaction( depositStatus: DepositStatus.CCTP_DEFAULT_STATE, isCctp: true, parentChainId: isDeposit ? sourceChainId : chainId, - childChainId: isDeposit ? chainId : sourceChainId, + childChainId: isDeposit ? destinationChainId : chainId, cctpData: { sourceChainId, attestationHash: messageSent.attestationHash, From 99ec6643cf292c24a96dab2a3fbb6d3554eabd17 Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 6 Dec 2023 14:57:58 +0100 Subject: [PATCH 09/72] fix --- packages/arb-token-bridge-ui/src/state/cctpState.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/state/cctpState.ts b/packages/arb-token-bridge-ui/src/state/cctpState.ts index ff0965e16b..23e74ba8c4 100644 --- a/packages/arb-token-bridge-ui/src/state/cctpState.ts +++ b/packages/arb-token-bridge-ui/src/state/cctpState.ts @@ -136,8 +136,8 @@ function parseTransferToMergedTransaction( tokenAddress: getUsdcTokenAddressFromSourceChainId(sourceChainId), depositStatus: DepositStatus.CCTP_DEFAULT_STATE, isCctp: true, - parentChainId: isDeposit ? sourceChainId : chainId, - childChainId: isDeposit ? destinationChainId : chainId, + parentChainId: isDeposit ? sourceChainId : destinationChainId, + childChainId: isDeposit ? destinationChainId : sourceChainId, cctpData: { sourceChainId, attestationHash: messageSent.attestationHash, From 316391b9f528f9d196fa3732fb6d53c4c27dac7c Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 6 Dec 2023 18:48:51 +0100 Subject: [PATCH 10/72] fetch by ts --- .../TransactionHistory/TransactionHistory.tsx | 6 +- .../TransactionHistoryTable.tsx | 77 ++++++++--------- .../src/components/common/Tab.tsx | 10 +-- .../src/hooks/useTransactionHistory.ts | 82 +++++++++++++------ 4 files changed, 95 insertions(+), 80 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx index d52dad55b5..f8a8f161f3 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx @@ -22,7 +22,7 @@ export const TransactionHistory = () => { const { address } = useAccount() const { - data: { transactions }, + data: { transactions, numberOfDays }, loading, completed, resume, @@ -75,7 +75,6 @@ export const TransactionHistory = () => { 0} > Pending transactions @@ -85,7 +84,6 @@ export const TransactionHistory = () => { roundedTabClasses, 'roundedTabLeft roundedTabRight' )} - showloader={loading && settledTransactions.length > 0} > Settled transactions @@ -99,6 +97,7 @@ export const TransactionHistory = () => { loading={loading} completed={completed} error={error} + numberOfDays={numberOfDays} resume={resume} rowHeight={94} rowHeightCustomDestinationAddress={126} @@ -110,6 +109,7 @@ export const TransactionHistory = () => { loading={loading} completed={completed} error={error} + numberOfDays={numberOfDays} resume={resume} rowHeight={85} rowHeightCustomDestinationAddress={117} diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx index d771a57828..035124a629 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx @@ -1,12 +1,6 @@ -import { PropsWithChildren, useCallback, useState } from 'react' +import { PropsWithChildren, useCallback } from 'react' import { twMerge } from 'tailwind-merge' -import { - AutoSizer, - Column, - ScrollEventData, - ScrollParams, - Table -} from 'react-virtualized' +import { AutoSizer, Column, Table } from 'react-virtualized' import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline' import { MergedTransaction } from '../../state/app/state' @@ -89,6 +83,7 @@ export const TransactionHistoryTable = ({ loading, completed, error, + numberOfDays, resume, rowHeight, rowHeightCustomDestinationAddress @@ -98,28 +93,15 @@ export const TransactionHistoryTable = ({ loading: boolean completed: boolean error: unknown + numberOfDays: number resume: () => void rowHeight: number rowHeightCustomDestinationAddress: number }) => { - const [scrollable, setScrollable] = useState(false) - const isTxHistoryEmpty = transactions.length === 0 const paused = !loading && !completed - function isScrolledToBottom(e: ScrollParams | ScrollEventData) { - const scrollPosition = e.scrollTop + e.clientHeight - return scrollPosition >= e.scrollHeight - } - - function handleScroll(e: ScrollParams | ScrollEventData) { - if (paused && isScrolledToBottom(e)) { - resume() - } - setScrollable(e.clientHeight > 0 && e.scrollHeight > e.clientHeight) - } - const getRowHeight = useCallback( (index: number) => { const tx = transactions[index] @@ -165,19 +147,17 @@ export const TransactionHistoryTable = ({ if (paused) { return (
-
+
We could not find any recent transactions!
-
- -
+
) } @@ -190,12 +170,33 @@ export const TransactionHistoryTable = ({ return (
+
+ {loading ? ( +
+ + Loading transactions... +
+ ) : ( +
+ + Showing transactions for the last {numberOfDays} day + {numberOfDays === 1 ? '' : 's'}. + + + +
+ )} +
{({ width, height }) => ( getRowHeight(index)} rowCount={transactions.length} headerHeight={52} @@ -270,16 +271,6 @@ export const TransactionHistoryTable = ({
)}
- {paused && !scrollable && ( -
- -
- )}
) } diff --git a/packages/arb-token-bridge-ui/src/components/common/Tab.tsx b/packages/arb-token-bridge-ui/src/components/common/Tab.tsx index 1e7d067c1f..31beb9456a 100644 --- a/packages/arb-token-bridge-ui/src/components/common/Tab.tsx +++ b/packages/arb-token-bridge-ui/src/components/common/Tab.tsx @@ -1,11 +1,8 @@ import { Tab } from '@headlessui/react' import { forwardRef, PropsWithChildren } from 'react' -import { Loader } from './atoms/Loader' export type TabButtonProps = PropsWithChildren< - React.ButtonHTMLAttributes & { - showloader?: boolean - } + React.ButtonHTMLAttributes > export const TabButton = forwardRef( @@ -19,10 +16,7 @@ export const TabButton = forwardRef( className={`${tabButtonClassName} ${props.className ?? ''}`} {...props} > - <> - {props.showloader && } - {props.children} - + {props.children} ) } diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 30cd14429e..0c90a7f75e 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -1,5 +1,6 @@ import useSWRImmutable from 'swr/immutable' -import { useCallback, useEffect, useState } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' +import dayjs from 'dayjs' import { ChainId, rpcURLs } from '../util/networks' import { StaticJsonRpcProvider } from '@ethersproject/providers' @@ -67,10 +68,10 @@ const multiChainFetchList: { parentChain: ChainId; chain: ChainId }[] = [ parentChain: ChainId.Ethereum, chain: ChainId.ArbitrumOne }, - // { - // parentChain: ChainId.Mainnet, - // chain: ChainId.ArbitrumNova - // }, + { + parentChain: ChainId.Ethereum, + chain: ChainId.ArbitrumNova + }, // Testnet { parentChain: ChainId.Goerli, @@ -79,16 +80,16 @@ const multiChainFetchList: { parentChain: ChainId; chain: ChainId }[] = [ { parentChain: ChainId.Sepolia, chain: ChainId.ArbitrumSepolia - } + }, // Orbit - // { - // parentChain: ChainId.ArbitrumGoerli, - // chain: ChainId.XaiTestnet - // } - // { - // parentChain: ChainId.ArbitrumSepolia, - // chain: ChainId.StylusTestnet - // } + { + parentChain: ChainId.ArbitrumGoerli, + chain: ChainId.XaiTestnet + }, + { + parentChain: ChainId.ArbitrumSepolia, + chain: ChainId.StylusTestnet + } ] function isWithdrawalFromSubgraph( @@ -338,14 +339,14 @@ const useTransactionHistoryWithoutStatuses = ( * This is done in small batches to safely meet RPC limits. */ export const useTransactionHistory = (address: `0x${string}` | undefined) => { - // max number of transactions mapped in parallel, for the same chain pair - // we can batch more than MAX_BATCH_SIZE at a time if they use a different RPC - // MAX_BATCH_SIZE means max number of transactions in a batch for a chain pair - const MAX_BATCH_SIZE = 5 - const PAUSE_SIZE = 20 + // max number of transactions mapped in parallel + const MAX_BATCH_SIZE = 10 + // Pause fetching after specified amount of days. User can resume fetching to get another batch. + const PAUSE_SIZE_DAYS = 30 const [transactions, setTransactions] = useState([]) const [page, setPage] = useState(0) + const [, setPauseCount] = useState(0) const [fetching, setFetching] = useState(true) const { data, loading, error } = useTransactionHistoryWithoutStatuses(address) @@ -353,8 +354,6 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { const { data: mapData, error: mapError } = useSWRImmutable( address && !loading ? ['complete_tx_list', address, page, data] : null, ([, , _page, _data]) => { - // TODO: Need to allow more than MAX_BATCH_SIZE if they are for diff chain pairs - // MAX_BATCH_SIZE refers to the same chain pair only const startIndex = _page * MAX_BATCH_SIZE const endIndex = startIndex + MAX_BATCH_SIZE @@ -364,6 +363,13 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { } ) + const latestTransactionDaysFromNow = useMemo(() => { + return dayjs().diff( + dayjs(transactions[transactions.length - 1]?.createdAt), + 'days' + ) + }, [transactions]) + function pause() { setFetching(false) } @@ -380,12 +386,32 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { return [...prevTransactions, ...newTransactions] }) + let didPause = false + + setPauseCount(prevPauseCount => { + const maxTimestamp = dayjs() + .subtract(PAUSE_SIZE_DAYS * (prevPauseCount + 1), 'days') + .valueOf() + + const latestTransaction = mapData[mapData.length - 1] + + if ( + latestTransaction && + latestTransaction.createdAt && + latestTransaction.createdAt <= maxTimestamp + ) { + // we fetched enough transactions to pause + pause() + didPause = true + return prevPauseCount + 1 + } + return prevPauseCount + }) + // mapped data is a full page, so we need to fetch more pages if (mapData.length === MAX_BATCH_SIZE) { setPage(prevPage => { - // we fetched enough transactions to pause - if (MAX_BATCH_SIZE * (prevPage + 1) >= PAUSE_SIZE) { - pause() + if (didPause) { return prevPage } return prevPage + 1 @@ -398,7 +424,7 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { if (loading || error) { return { - data: { transactions: [], total: undefined }, + data: { transactions: [], total: undefined, numberOfDays: 0 }, loading, error, completed: true, @@ -408,7 +434,11 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { } return { - data: { transactions, total: data.length }, + data: { + transactions, + total: data.length, + numberOfDays: latestTransactionDaysFromNow + }, loading: fetching, completed: transactions.length === data.length, error: mapError ?? error, From 52b9d25f447c0c8c0ece830ad1d1686ecf07b8cf Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 6 Dec 2023 18:53:57 +0100 Subject: [PATCH 11/72] clean up --- .../TransactionHistory/TransactionHistoryTable.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx index 81315c25cd..518e8ecc5a 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx @@ -117,6 +117,8 @@ export const TransactionHistoryTable = ({ [transactions, rowHeight, rowHeightCustomDestinationAddress] ) + const numberOfDaysString = `${numberOfDays}${numberOfDays === 1 ? 's' : ''}` + if (isTxHistoryEmpty) { if (error) { return ( @@ -149,7 +151,8 @@ export const TransactionHistoryTable = ({
- We could not find any recent transactions! + Looks like there are no transactions in the last{' '} + {numberOfDaysString}.
+ {!completed && ( + + )}
)}
From b4dbcb250284e5ff1a82fe285310b0a1c5d9eb96 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 7 Dec 2023 14:25:47 +0100 Subject: [PATCH 18/72] tx history includer --- .../components/MainContent/MainContent.tsx | 7 +- .../TransactionHistory/TransactionHistory.tsx | 18 +-- .../TransactionsTableDepositRow.tsx | 5 +- .../components/TransactionHistory/helpers.ts | 3 + .../TransferPanel/TransferPanel.tsx | 39 ++----- .../src/hooks/useArbTokenBridge.ts | 108 +++++++++++------- .../src/hooks/useTransactionHistory.ts | 79 ++++++++++--- 7 files changed, 162 insertions(+), 97 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx index f086db0651..a8dd47a20f 100644 --- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx +++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx @@ -1,5 +1,6 @@ import { motion, AnimatePresence } from 'framer-motion' import useLocalStorage from '@rehooks/local-storage' +import { useAccount } from 'wagmi' import { TransferPanel } from '../TransferPanel/TransferPanel' import { SidePanel } from '../common/SidePanel' @@ -7,6 +8,7 @@ import { useAppContextActions, useAppContextState } from '../App/AppContext' import { ArbitrumStats, statsLocalStorageKey } from './ArbitrumStats' import { SettingsDialog } from '../common/SettingsDialog' import { TransactionHistory } from '../TransactionHistory/TransactionHistory' +import { useTransactionHistory } from '../../hooks/useTransactionHistory' export const motionDivProps = { layout: true, @@ -25,6 +27,7 @@ export const motionDivProps = { } export function MainContent() { + const { address } = useAccount() const { closeTransactionHistoryPanel } = useAppContextActions() const { layout: { isTransactionHistoryPanelVisible } @@ -33,6 +36,8 @@ export function MainContent() { const [isArbitrumStatsVisible] = useLocalStorage(statsLocalStorageKey) + const transactionHistoryProps = useTransactionHistory(address) + return (
@@ -54,7 +59,7 @@ export function MainContent() { onClose={closeTransactionHistoryPanel} scrollable={false} > - + {/* Settings panel */} diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx index f8a8f161f3..4e95eadada 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx @@ -1,9 +1,8 @@ import { useMemo } from 'react' import { Tab } from '@headlessui/react' import { twMerge } from 'tailwind-merge' -import { useAccount } from 'wagmi' -import { useTransactionHistory } from '../../hooks/useTransactionHistory' +import { TransactionHistoryParams } from '../../hooks/useTransactionHistory' import { TransactionHistoryTable } from './TransactionHistoryTable' import { isTxClaimable, @@ -18,16 +17,19 @@ import { TabButton } from '../common/Tab' const roundedTabClasses = 'roundedTab ui-not-selected:arb-hover relative flex flex-row flex-nowrap items-center gap-0.5 md:gap-2 rounded-tl-lg rounded-tr-lg px-2 md:px-4 py-2 text-base ui-selected:bg-white ui-not-selected:text-white justify-center md:justify-start grow md:grow-0' -export const TransactionHistory = () => { - const { address } = useAccount() - +export const TransactionHistory = ({ + props +}: { + props: TransactionHistoryParams & { address: `0x${string}` | undefined } +}) => { const { data: { transactions, numberOfDays }, + address, loading, completed, - resume, - error - } = useTransactionHistory(address) + error, + resume + } = props const groupedTransactions = useMemo( () => diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDepositRow.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDepositRow.tsx index 26ac737df3..7c2d2383ab 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDepositRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDepositRow.tsx @@ -280,9 +280,10 @@ export function TransactionsTableDepositRow({ { return new Promise(res => setTimeout(res, ms)) @@ -107,6 +110,8 @@ export const useArbTokenBridge = ( ContractStorage | undefined >(undefined) + const { addPendingTransaction } = useTransactionHistory(walletAddress) + const { eth: [, updateEthL1Balance], erc20: [, updateErc20L1Balance] @@ -208,18 +213,24 @@ export const useArbTokenBridge = ( return error.message } - addTransaction({ - type: 'deposit-l1', - status: 'pending', - value: utils.formatUnits(amount, nativeCurrency.decimals), - txID: tx.hash, - assetName: nativeCurrency.symbol, - assetType: AssetType.ETH, + addPendingTransaction({ sender: walletAddress, - // TODO: change to destinationAddress ?? walletAddress when enabling ETH transfers to a custom address destination: walletAddress, - l1NetworkID, - l2NetworkID + direction: 'deposit-l1', + status: 'pending', + createdAt: dayjs().valueOf(), + resolvedAt: null, + txId: tx.hash, + asset: nativeCurrency.symbol, + assetType: AssetType.ETH, + value: utils.formatUnits(amount, nativeCurrency.decimals), + uniqueId: null, + isWithdrawal: false, + blockNum: null, + tokenAddress: null, + depositStatus: DepositStatus.L1_PENDING, + parentChainId: Number(l1NetworkID), + childChainId: Number(l2NetworkID) }) const receipt = await tx.wait() @@ -271,19 +282,23 @@ export const useArbTokenBridge = ( txLifecycle.onTxSubmit(tx) } - addTransaction({ - type: 'withdraw', - status: 'pending', - value: utils.formatUnits(amount, nativeCurrency.decimals), - txID: tx.hash, - assetName: nativeCurrency.symbol, - assetType: AssetType.ETH, + addPendingTransaction({ sender: walletAddress, - // TODO: change to destinationAddress ?? walletAddress when enabling ETH transfers to a custom address destination: walletAddress, - blockNumber: tx.blockNumber, - l1NetworkID, - l2NetworkID + direction: 'withdraw', + status: WithdrawalStatus.UNCONFIRMED, + createdAt: dayjs().valueOf(), + resolvedAt: null, + txId: tx.hash, + asset: nativeCurrency.symbol, + assetType: AssetType.ETH, + value: utils.formatUnits(amount, nativeCurrency.decimals), + uniqueId: null, + isWithdrawal: true, + blockNum: null, + tokenAddress: null, + parentChainId: Number(l1NetworkID), + childChainId: Number(l2NetworkID) }) const receipt = await tx.wait() @@ -463,18 +478,25 @@ export const useArbTokenBridge = ( if (txLifecycle?.onTxSubmit) { txLifecycle.onTxSubmit(tx) } - addTransaction({ - type: 'deposit-l1', + + addPendingTransaction({ + sender: walletAddress, + destination: destinationAddress ?? walletAddress, + direction: 'deposit-l1', status: 'pending', - value: utils.formatUnits(amount, decimals), - txID: tx.hash, - assetName: symbol, + createdAt: dayjs().valueOf(), + resolvedAt: null, + txId: tx.hash, + asset: symbol, assetType: AssetType.ERC20, + value: utils.formatUnits(amount, decimals), + depositStatus: DepositStatus.L1_PENDING, + uniqueId: null, + isWithdrawal: false, + blockNum: null, tokenAddress: erc20L1Address, - sender: walletAddress, - destination: destinationAddress ?? walletAddress, - l1NetworkID, - l2NetworkID + parentChainId: Number(l1NetworkID), + childChainId: Number(l2NetworkID) }) const receipt = await tx.wait() @@ -561,19 +583,25 @@ export const useArbTokenBridge = ( txLifecycle.onTxSubmit(tx) } - addTransaction({ - type: 'withdraw', - status: 'pending', - value: utils.formatUnits(amount, decimals), - txID: tx.hash, - assetName: symbol, - assetType: AssetType.ERC20, + addPendingTransaction({ sender: walletAddress, destination: destinationAddress ?? walletAddress, - blockNumber: tx.blockNumber, - l1NetworkID, - l2NetworkID + direction: 'withdraw', + status: WithdrawalStatus.UNCONFIRMED, + createdAt: dayjs().valueOf(), + resolvedAt: null, + txId: tx.hash, + asset: symbol, + assetType: AssetType.ERC20, + value: utils.formatUnits(amount, decimals), + uniqueId: null, + isWithdrawal: true, + blockNum: null, + tokenAddress: null, + parentChainId: Number(l1NetworkID), + childChainId: Number(l2NetworkID) }) + const receipt = await tx.wait() if (txLifecycle?.onTxConfirm) { diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index ed52d6d705..3a0b74ecb7 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -1,3 +1,4 @@ +import { mutate, useSWRConfig, unstable_serialize } from 'swr' import useSWRImmutable from 'swr/immutable' import { useCallback, useEffect, useMemo, useState } from 'react' import dayjs from 'dayjs' @@ -28,6 +29,7 @@ import { import { FetchWithdrawalsFromSubgraphResult } from '../util/withdrawals/fetchWithdrawalsFromSubgraph' import { updateAdditionalDepositData } from '../util/deposits/helpers' import { useCctpFetching } from '../state/cctpState' +import { isTxPending } from '../components/TransactionHistory/helpers' const PAGE_SIZE = 100 @@ -41,6 +43,19 @@ export type Withdrawal = type DepositOrWithdrawal = Deposit | Withdrawal type Transfer = DepositOrWithdrawal | MergedTransaction +export type TransactionHistoryParams = { + data: { + transactions: MergedTransaction[] + numberOfDays: number + } + loading: boolean + completed: boolean + error: unknown + pause: () => void + resume: () => void + addPendingTransaction: (tx: MergedTransaction) => void +} + function getStandardizedTimestampByTx(tx: Transfer) { if (isCctpTransfer(tx)) { return (tx.createdAt ?? 0) / 1_000 @@ -339,23 +354,33 @@ const useTransactionHistoryWithoutStatuses = ( * Maps additional info to previously fetches transaction history, starting with the earliest data. * This is done in small batches to safely meet RPC limits. */ -export const useTransactionHistory = (address: `0x${string}` | undefined) => { +export const useTransactionHistory = ( + address: `0x${string}` | undefined +): TransactionHistoryParams => { // max number of transactions mapped in parallel const MAX_BATCH_SIZE = 10 // Pause fetching after specified amount of days. User can resume fetching to get another batch. const PAUSE_SIZE_DAYS = 30 - const [transactions, setTransactions] = useState([]) + // const [transactions, setTransactions] = useState([]) const [page, setPage] = useState(0) const [, setPauseCount] = useState(0) const [fetching, setFetching] = useState(true) const { data, loading, error } = useTransactionHistoryWithoutStatuses(address) + const { cache } = useSWRConfig() + + const getCacheKey = useCallback( + (pageNumber: number) => { + return address + ? (['complete_tx_list', address, pageNumber, data] as const) + : null + }, + [address, data] + ) const { data: mapData, error: mapError } = useSWRImmutable( - address && !loading && fetching - ? ['complete_tx_list', address, page, data] - : null, + !loading && fetching ? getCacheKey(page) : null, ([, , _page, _data]) => { const startIndex = _page * MAX_BATCH_SIZE const endIndex = startIndex + MAX_BATCH_SIZE @@ -366,6 +391,19 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { } ) + const transactions: MergedTransaction[] = useMemo(() => { + const allTransactions: MergedTransaction[] = [] + + for (let i = 0; i < page; i++) { + const txsForPage = cache.get(unstable_serialize(getCacheKey(i))) + ?.data as MergedTransaction[] + + allTransactions.push(...txsForPage) + } + + return allTransactions + }, [page, cache, getCacheKey]) + const latestTransactionDaysFromNow = useMemo(() => { return dayjs().diff( dayjs(transactions[transactions.length - 1]?.createdAt), @@ -381,13 +419,25 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { setFetching(true) } + const addPendingTransaction = useCallback( + (tx: MergedTransaction) => { + if (!isTxPending(tx)) { + return + } + + const cacheKey = getCacheKey(0) + + const prevTransactions = + (cache.get(unstable_serialize(cacheKey)) + ?.data as MergedTransaction[]) || [] + + mutate(cacheKey, [tx, ...prevTransactions], false) + }, + [cache, getCacheKey] + ) + useEffect(() => { if (mapData) { - setTransactions(prevTransactions => { - const newTransactions = mapData.filter(Boolean) as MergedTransaction[] - return [...prevTransactions, ...newTransactions] - }) - setPauseCount(prevPauseCount => { const latestTransaction = mapData[mapData.length - 1] const maxTimestamp = dayjs() @@ -417,25 +467,26 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { if (loading || error) { return { - data: { transactions: [], total: undefined, numberOfDays: 0 }, + data: { transactions: [], numberOfDays: 0 }, loading, error, completed: true, pause, - resume + resume, + addPendingTransaction } } return { data: { transactions, - total: data.length, numberOfDays: latestTransactionDaysFromNow }, loading: fetching, completed: transactions.length === data.length, error: mapError ?? error, pause, - resume + resume, + addPendingTransaction } } From d2772c00d45a195beec54df255a8d0090428bc95 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 7 Dec 2023 14:38:05 +0100 Subject: [PATCH 19/72] unused state --- packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 3a0b74ecb7..58073a066a 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -362,7 +362,6 @@ export const useTransactionHistory = ( // Pause fetching after specified amount of days. User can resume fetching to get another batch. const PAUSE_SIZE_DAYS = 30 - // const [transactions, setTransactions] = useState([]) const [page, setPage] = useState(0) const [, setPauseCount] = useState(0) const [fetching, setFetching] = useState(true) From fdd545bb185ca07d3cd8b0bb17a2d6551a7a8be6 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 7 Dec 2023 14:52:47 +0100 Subject: [PATCH 20/72] display dest network if L1 tx pending --- .../TransactionsTableDepositRow.tsx | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDepositRow.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDepositRow.tsx index 7c2d2383ab..7ec83b5ae9 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDepositRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDepositRow.tsx @@ -33,12 +33,20 @@ function DepositRowStatus({ tx }: { tx: MergedTransaction }) { switch (tx.depositStatus) { case DepositStatus.L1_PENDING: return ( - - Pending - +
+ + Pending + + + Pending + +
) case DepositStatus.L1_FAILURE: @@ -185,22 +193,24 @@ function DepositRowTxID({ tx }: { tx: MergedTransaction }) { - {l2TxHash && ( - - To - - {getNetworkName(tx.childChainId)}: + + To + + {getNetworkName(tx.childChainId)}: + {l2TxHash ? ( {shortenTxHash(l2TxHash)} - - )} + ) : ( + <>Pending + )} +
) } From cd06852b2e6ba659d43dcbcbc4e95332117574d4 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 7 Dec 2023 16:35:55 +0100 Subject: [PATCH 21/72] not needed classname --- .../components/TransactionHistory/TransactionHistoryTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx index 5836c11a14..9be4b78b39 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx @@ -174,7 +174,7 @@ export const TransactionHistoryTable = ({ } return ( -
+
{loading ? (
From 809ddb2b727fa45a16e69fe22d8c5bf6bb2d7be7 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 7 Dec 2023 17:09:21 +0100 Subject: [PATCH 22/72] add comments to the pausing logic --- .../src/hooks/useTransactionHistory.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index ed52d6d705..77a718b300 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -388,21 +388,30 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { return [...prevTransactions, ...newTransactions] }) + // we use setter to access the previous value to get accurate reading + // we will also increment it if fetching gets paused setPauseCount(prevPauseCount => { + // get the latest transaction, we will use its timestamp to see if we went past maximum number of fetched days const latestTransaction = mapData[mapData.length - 1] + // max timestamp is our threshold where we stop fetching transactions for this iteration + // it gets incremented by PAUSE_SIZE_DAYS with each iteration const maxTimestamp = dayjs() .subtract(PAUSE_SIZE_DAYS * (prevPauseCount + 1), 'days') .valueOf() + // check if our latest transaction is past our lookup threshold if ( latestTransaction && latestTransaction.createdAt && latestTransaction.createdAt <= maxTimestamp ) { + // if yes, we pause + // we also increase pause count for so the next iteration can calculate max timestamp pause() return prevPauseCount + 1 } + // otherwise do not pause, we also keep the pause count as-is return prevPauseCount }) From 51a7794703e87d9844b110228cdd349878aa2c33 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 7 Dec 2023 17:11:41 +0100 Subject: [PATCH 23/72] comment typo --- packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 77a718b300..e64998a85d 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -406,7 +406,7 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { latestTransaction.createdAt <= maxTimestamp ) { // if yes, we pause - // we also increase pause count for so the next iteration can calculate max timestamp + // we also increment pause count so the next iteration can calculate max timestamp pause() return prevPauseCount + 1 } From 68a6fbacbc592c20433119ead03cbf950e2dfe53 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 7 Dec 2023 19:37:01 +0100 Subject: [PATCH 24/72] init --- .../components/MainContent/MainContent.tsx | 18 +++ .../components/TransactionHistory/helpers.ts | 118 +++++++++++++++++- .../src/hooks/useTransactionHistory.ts | 86 +++++++++++-- .../src/state/app/utils.ts | 21 +++- 4 files changed, 228 insertions(+), 15 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx index a8dd47a20f..e9e84613e0 100644 --- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx +++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx @@ -1,3 +1,4 @@ +import { useEffect } from 'react' import { motion, AnimatePresence } from 'framer-motion' import useLocalStorage from '@rehooks/local-storage' import { useAccount } from 'wagmi' @@ -9,6 +10,7 @@ import { ArbitrumStats, statsLocalStorageKey } from './ArbitrumStats' import { SettingsDialog } from '../common/SettingsDialog' import { TransactionHistory } from '../TransactionHistory/TransactionHistory' import { useTransactionHistory } from '../../hooks/useTransactionHistory' +import { isTxPending } from '../TransactionHistory/helpers' export const motionDivProps = { layout: true, @@ -37,6 +39,22 @@ export function MainContent() { useLocalStorage(statsLocalStorageKey) const transactionHistoryProps = useTransactionHistory(address) + const { + data: { transactions }, + updatePendingTransaction + } = transactionHistoryProps + + useEffect(() => { + const interval = setInterval(() => { + transactions.forEach(tx => { + if (isTxPending(tx)) { + updatePendingTransaction(tx) + } + }) + }, 10_000) + + return () => clearInterval(interval) + }, [JSON.stringify(transactions), JSON.stringify(updatePendingTransaction)]) return (
diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts index 70248a66a0..4a99096b79 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts @@ -1,11 +1,22 @@ import dayjs from 'dayjs' +import { + StaticJsonRpcProvider, + TransactionReceipt +} from '@ethersproject/providers' +import { EthDepositStatus, L1ToL2MessageStatus } from '@arbitrum/sdk' +import { EthDepositMessage } from '@arbitrum/sdk/dist/lib/message/L1ToL2Message' + import { DepositStatus, MergedTransaction, WithdrawalStatus } from '../../state/app/state' -import { isNetwork } from '../../util/networks' +import { ChainId, isNetwork, rpcURLs } from '../../util/networks' import { isCctpTransfer } from '../../hooks/useTransactionHistory' +import { getWagmiChain } from '../../util/wagmi/getWagmiChain' +import { getL1ToL2MessageDataFromL1TxHash } from '../../util/deposits/helpers' +import { AssetType } from '../../hooks/arbTokenBridge.types' +import { getDepositStatus } from '../../state/app/utils' export enum StatusLabel { PENDING = 'Pending', @@ -78,6 +89,111 @@ export function getDestChainId(tx: MergedTransaction) { return isDeposit(tx) ? tx.childChainId : tx.parentChainId } +export function getProvider(chainId: ChainId) { + const rpcUrl = + rpcURLs[chainId] ?? getWagmiChain(chainId).rpcUrls.default.http[0] + return new StaticJsonRpcProvider(rpcUrl) +} + +export function isSameTransaction( + tx_1: MergedTransaction, + tx_2: MergedTransaction +) { + return ( + tx_1.txId === tx_2.txId && + tx_1.parentChainId === tx_2.parentChainId && + tx_1.childChainId === tx_2.childChainId + ) +} + +export function getTxReceipt(tx: MergedTransaction) { + const parentChainProvider = getProvider(tx.parentChainId) + const childChainProvider = getProvider(tx.childChainId) + + const provider = tx.isWithdrawal ? childChainProvider : parentChainProvider + + return provider.getTransactionReceipt(tx.txId) +} + +function getWithdrawalStatusFromReceipt( + receipt: TransactionReceipt +): WithdrawalStatus | undefined { + switch (receipt.status) { + case 0: + return WithdrawalStatus.FAILURE + case 1: + return WithdrawalStatus.UNCONFIRMED + default: + return undefined + } +} + +export async function getUpdatedEthDeposit( + tx: MergedTransaction +): Promise { + if (!isTxPending(tx) || tx.assetType !== AssetType.ETH || tx.isWithdrawal) { + return tx + } + + const { l1ToL2Msg } = await getL1ToL2MessageDataFromL1TxHash({ + depositTxId: tx.txId, + isEthDeposit: true, + l1Provider: getProvider(tx.parentChainId), + l2Provider: getProvider(tx.childChainId) + }) + + if (!l1ToL2Msg) { + return tx + } + + const status = await l1ToL2Msg?.status() + const isDeposited = status === EthDepositStatus.DEPOSITED + + const newDeposit: MergedTransaction = { + ...tx, + status: status === 1 ? 'success' : tx.status, + l1ToL2MsgData: { + fetchingUpdate: false, + status: isDeposited + ? L1ToL2MessageStatus.FUNDS_DEPOSITED_ON_L2 + : L1ToL2MessageStatus.NOT_YET_CREATED, + retryableCreationTxID: (l1ToL2Msg as EthDepositMessage).l2DepositTxHash, + // Only show `l2TxID` after the deposit is confirmed + l2TxID: isDeposited + ? (l1ToL2Msg as EthDepositMessage).l2DepositTxHash + : undefined + } + } + + return { + ...newDeposit, + depositStatus: getDepositStatus(newDeposit) + } +} + +export async function getUpdatedEthWithdrawal( + tx: MergedTransaction +): Promise { + if (!isTxPending(tx) || tx.assetType !== AssetType.ETH || !tx.isWithdrawal) { + return tx + } + + const receipt = await getTxReceipt(tx) + + if (receipt) { + const newStatus = getWithdrawalStatusFromReceipt(receipt) + + if (typeof newStatus !== 'undefined') { + return { + ...tx, + status: newStatus + } + } + } + + return tx +} + export function getTxStatusLabel(tx: MergedTransaction): StatusLabel { if (isDeposit(tx)) { switch (tx.depositStatus) { diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 58073a066a..9c878ecf17 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -3,9 +3,7 @@ import useSWRImmutable from 'swr/immutable' import { useCallback, useEffect, useMemo, useState } from 'react' import dayjs from 'dayjs' -import { ChainId, rpcURLs } from '../util/networks' -import { StaticJsonRpcProvider } from '@ethersproject/providers' -import { getWagmiChain } from '../util/wagmi/getWagmiChain' +import { ChainId } from '../util/networks' import { fetchWithdrawals } from '../util/withdrawals/fetchWithdrawals' import { fetchDeposits } from '../util/deposits/fetchDeposits' import { @@ -29,7 +27,13 @@ import { import { FetchWithdrawalsFromSubgraphResult } from '../util/withdrawals/fetchWithdrawalsFromSubgraph' import { updateAdditionalDepositData } from '../util/deposits/helpers' import { useCctpFetching } from '../state/cctpState' -import { isTxPending } from '../components/TransactionHistory/helpers' +import { + getProvider, + getUpdatedEthDeposit, + getUpdatedEthWithdrawal, + isSameTransaction, + isTxPending +} from '../components/TransactionHistory/helpers' const PAGE_SIZE = 100 @@ -54,6 +58,7 @@ export type TransactionHistoryParams = { pause: () => void resume: () => void addPendingTransaction: (tx: MergedTransaction) => void + updatePendingTransaction: (tx: MergedTransaction) => void } function getStandardizedTimestampByTx(tx: Transfer) { @@ -173,12 +178,6 @@ async function transformTransaction(tx: Transfer): Promise { ) } -function getProvider(chainId: ChainId) { - const rpcUrl = - rpcURLs[chainId] ?? getWagmiChain(chainId).rpcUrls.default.http[0] - return new StaticJsonRpcProvider(rpcUrl) -} - /** * Fetches transaction history only for deposits and withdrawals, without their statuses. */ @@ -418,6 +417,17 @@ export const useTransactionHistory = ( setFetching(true) } + const getTransactionsForPage = useCallback( + (pageNumber: number) => { + const cacheKey = getCacheKey(pageNumber) + return ( + (cache.get(unstable_serialize(cacheKey)) + ?.data as MergedTransaction[]) || [] + ) + }, + [cache, getCacheKey] + ) + const addPendingTransaction = useCallback( (tx: MergedTransaction) => { if (!isTxPending(tx)) { @@ -435,6 +445,56 @@ export const useTransactionHistory = ( [cache, getCacheKey] ) + const updateCachedTransaction = useCallback( + (newTx: MergedTransaction) => { + for (let i = 0; i < page; i++) { + const oldTxsForPage = getTransactionsForPage(i) + // see if tx exist in cache for this page + const foundInCache = !!oldTxsForPage.find(oldTx => + isSameTransaction(oldTx, newTx) + ) + + // if yes, then we replace it with the new transaction + if (foundInCache) { + const newTxs = oldTxsForPage.map(oldTx => { + if (isSameTransaction(oldTx, newTx)) { + return newTx + } + return oldTx + }) + + mutate(getCacheKey(i), newTxs, false) + break + } + } + }, + [getCacheKey, getTransactionsForPage, page] + ) + + const updatePendingTransaction = useCallback( + async (tx: MergedTransaction) => { + if (!isTxPending(tx)) { + return + } + + const foundInCache = !!transactions.find(t => isSameTransaction(t, tx)) + + if (!foundInCache) { + // tx does not exist + return + } + + if (tx.isWithdrawal) { + const updatedEthWithdrawal = await getUpdatedEthWithdrawal(tx) + updateCachedTransaction(updatedEthWithdrawal) + } else { + const updatedEthDeposit = await getUpdatedEthDeposit(tx) + updateCachedTransaction(updatedEthDeposit) + } + }, + [transactions, updateCachedTransaction] + ) + useEffect(() => { if (mapData) { setPauseCount(prevPauseCount => { @@ -472,7 +532,8 @@ export const useTransactionHistory = ( completed: true, pause, resume, - addPendingTransaction + addPendingTransaction, + updatePendingTransaction } } @@ -486,6 +547,7 @@ export const useTransactionHistory = ( error: mapError ?? error, pause, resume, - addPendingTransaction + addPendingTransaction, + updatePendingTransaction } } diff --git a/packages/arb-token-bridge-ui/src/state/app/utils.ts b/packages/arb-token-bridge-ui/src/state/app/utils.ts index 3a8c901446..b3ef70a595 100644 --- a/packages/arb-token-bridge-ui/src/state/app/utils.ts +++ b/packages/arb-token-bridge-ui/src/state/app/utils.ts @@ -21,8 +21,25 @@ export const outgoingStateToString = { [OutgoingMessageState.EXECUTED]: 'Executed' } -export const getDepositStatus = (tx: Transaction) => { - if (tx.type !== 'deposit' && tx.type !== 'deposit-l1') return undefined +export const getDepositStatus = (tx: Transaction | MergedTransaction) => { + const transaction = tx as Transaction + const mergedTransaction = tx as MergedTransaction + + if ( + typeof transaction.type !== 'undefined' && + transaction.type !== 'deposit' && + transaction.type !== 'deposit-l1' + ) { + return undefined + } + + if ( + typeof mergedTransaction.isWithdrawal !== undefined && + mergedTransaction.isWithdrawal + ) { + return undefined + } + if (tx.status === 'failure') { return DepositStatus.L1_FAILURE } From 5e8bd990a73622224745d7d89ffe8454b304455e Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 7 Dec 2023 20:08:55 +0100 Subject: [PATCH 25/72] clean up --- .../src/hooks/useTransactionHistory.ts | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 9c878ecf17..f55cba22da 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -454,18 +454,21 @@ export const useTransactionHistory = ( isSameTransaction(oldTx, newTx) ) - // if yes, then we replace it with the new transaction - if (foundInCache) { - const newTxs = oldTxsForPage.map(oldTx => { - if (isSameTransaction(oldTx, newTx)) { - return newTx - } - return oldTx - }) - - mutate(getCacheKey(i), newTxs, false) - break + if (!foundInCache) { + // tx does not exist + return } + + // if exists, then we replace it with the new transaction + const newTxs = oldTxsForPage.map(oldTx => { + if (isSameTransaction(oldTx, newTx)) { + return newTx + } + return oldTx + }) + + mutate(getCacheKey(i), newTxs, false) + break } }, [getCacheKey, getTransactionsForPage, page] From 97360ab39059246b71fb2783c018783f42756271 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 8 Dec 2023 14:23:27 +0100 Subject: [PATCH 26/72] updaters --- .../components/TransactionHistory/helpers.ts | 89 +++++++++++++- .../src/hooks/useArbTokenBridge.ts | 109 +----------------- .../src/hooks/useTransactionHistory.ts | 19 ++- 3 files changed, 99 insertions(+), 118 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts index 4a99096b79..528be64271 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts @@ -4,7 +4,10 @@ import { TransactionReceipt } from '@ethersproject/providers' import { EthDepositStatus, L1ToL2MessageStatus } from '@arbitrum/sdk' -import { EthDepositMessage } from '@arbitrum/sdk/dist/lib/message/L1ToL2Message' +import { + EthDepositMessage, + L1ToL2MessageReader +} from '@arbitrum/sdk/dist/lib/message/L1ToL2Message' import { DepositStatus, @@ -131,7 +134,12 @@ function getWithdrawalStatusFromReceipt( export async function getUpdatedEthDeposit( tx: MergedTransaction ): Promise { - if (!isTxPending(tx) || tx.assetType !== AssetType.ETH || tx.isWithdrawal) { + if ( + !isTxPending(tx) || + tx.assetType !== AssetType.ETH || + tx.isWithdrawal || + tx.isCctp + ) { return tx } @@ -143,7 +151,16 @@ export async function getUpdatedEthDeposit( }) if (!l1ToL2Msg) { - return tx + const receipt = await getTxReceipt(tx) + + if (!receipt || receipt.status !== 0) { + // not failure + return tx + } + + // failure + const newDeposit = { ...tx, status: 'failure' } + return { ...newDeposit, depositStatus: getDepositStatus(newDeposit) } } const status = await l1ToL2Msg?.status() @@ -151,7 +168,7 @@ export async function getUpdatedEthDeposit( const newDeposit: MergedTransaction = { ...tx, - status: status === 1 ? 'success' : tx.status, + status: 'success', l1ToL2MsgData: { fetchingUpdate: false, status: isDeposited @@ -171,10 +188,70 @@ export async function getUpdatedEthDeposit( } } -export async function getUpdatedEthWithdrawal( +export async function getUpdatedTokenDeposit( + tx: MergedTransaction +): Promise { + if ( + !isTxPending(tx) || + tx.assetType !== AssetType.ERC20 || + tx.isWithdrawal || + tx.isCctp + ) { + return tx + } + + const { l1ToL2Msg } = await getL1ToL2MessageDataFromL1TxHash({ + depositTxId: tx.txId, + isEthDeposit: false, + l1Provider: getProvider(tx.parentChainId), + l2Provider: getProvider(tx.childChainId) + }) + const _l1ToL2Msg = l1ToL2Msg as L1ToL2MessageReader + + if (!l1ToL2Msg) { + const receipt = await getTxReceipt(tx) + + if (!receipt || receipt.status !== 0) { + // not failure + return tx + } + + // failure + const newDeposit = { ...tx, status: 'failure' } + return { ...newDeposit, depositStatus: getDepositStatus(newDeposit) } + } + + const res = await _l1ToL2Msg.getSuccessfulRedeem() + + const l2TxID = (() => { + if (res.status === L1ToL2MessageStatus.REDEEMED) { + return res.l2TxReceipt.transactionHash + } else { + return undefined + } + })() + + const newDeposit: MergedTransaction = { + ...tx, + status: [3, 4].includes(res.status) ? 'success' : tx.status, + l1ToL2MsgData: { + status: res.status, + l2TxID, + fetchingUpdate: false, + retryableCreationTxID: _l1ToL2Msg.retryableCreationId + } + } + + return { + ...newDeposit, + depositStatus: getDepositStatus(newDeposit) + } +} + +export async function getUpdatedWithdrawal( tx: MergedTransaction ): Promise { - if (!isTxPending(tx) || tx.assetType !== AssetType.ETH || !tx.isWithdrawal) { + if (!isTxPending(tx) || !tx.isWithdrawal || tx.isCctp) { return tx } diff --git a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts index 22c4ca6ed1..1f422ba12c 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts @@ -6,12 +6,7 @@ import { JsonRpcProvider } from '@ethersproject/providers' import { useLocalStorage } from '@rehooks/local-storage' import { TokenList } from '@uniswap/token-lists' import { MaxUint256 } from '@ethersproject/constants' -import { - EthBridger, - Erc20Bridger, - L1ToL2MessageStatus, - L2ToL1Message -} from '@arbitrum/sdk' +import { EthBridger, Erc20Bridger, L2ToL1Message } from '@arbitrum/sdk' import { L1EthDepositTransaction } from '@arbitrum/sdk/dist/lib/message/L1Transaction' import { ERC20__factory } from '@arbitrum/sdk/dist/lib/abi/factories/ERC20__factory' import { EventArgs } from '@arbitrum/sdk/dist/lib/dataEntities/event' @@ -19,7 +14,7 @@ import { L2ToL1TransactionEvent } from '@arbitrum/sdk/dist/lib/message/L2ToL1Mes import { L2ToL1TransactionEvent as ClassicL2ToL1TransactionEvent } from '@arbitrum/sdk/dist/lib/abi/ArbSys' import dayjs from 'dayjs' -import useTransactions, { L1ToL2MessageData } from './useTransactions' +import useTransactions from './useTransactions' import { ArbTokenBridge, AssetType, @@ -32,7 +27,6 @@ import { L2ToL1EventResult, L1EthDepositTransactionLifecycle, L1ContractCallTransactionLifecycle, - NodeBlockDeadlineStatusTypes, ArbTokenBridgeEth, ArbTokenBridgeToken } from './arbTokenBridge.types' @@ -239,20 +233,6 @@ export const useArbTokenBridge = ( txLifecycle.onTxConfirm(receipt) } - const [ethDepositMessage] = await receipt.getEthDeposits(l2.provider) - - if (!ethDepositMessage) { - return - } - - const l1ToL2MsgData: L1ToL2MessageData = { - fetchingUpdate: false, - status: L1ToL2MessageStatus.NOT_YET_CREATED, - retryableCreationTxID: ethDepositMessage.l2DepositTxHash, - l2TxID: undefined - } - - updateTransaction(receipt, tx, l1ToL2MsgData) updateEthBalances() if (nativeCurrency.isCustom) { @@ -307,44 +287,8 @@ export const useArbTokenBridge = ( txLifecycle.onTxConfirm(receipt) } - updateTransaction(receipt, tx) updateEthBalances() - const l2ToL1Events = receipt.getL2ToL1Events() - - if (l2ToL1Events.length === 1) { - const l2ToL1EventResult = l2ToL1Events[0] - - if (!l2ToL1EventResult) { - return - } - - const id = getUniqueIdOrHashFromEvent(l2ToL1EventResult).toString() - - const outgoingMessageState = OutgoingMessageState.UNCONFIRMED - const l2ToL1EventResultPlus: L2ToL1EventResultPlus = { - ...l2ToL1EventResult, - sender: tx.from, - // TODO: add destinationAddress: destinationAddress ?? walletAddress when enabling ETH transfers to a custom address - type: AssetType.ETH, - value: amount, - outgoingMessageState, - symbol: nativeCurrency.symbol, - decimals: nativeCurrency.decimals, - nodeBlockDeadline: NodeBlockDeadlineStatusTypes.NODE_NOT_CREATED, - l2TxHash: tx.hash, - parentChainId: Number(l1NetworkID), - childChainId: Number(l2NetworkID) - } - - setPendingWithdrawalMap(oldPendingWithdrawalsMap => { - return { - ...oldPendingWithdrawalsMap, - [id]: l2ToL1EventResultPlus - } - }) - } - return receipt } catch (error) { if (txLifecycle?.onTxError) { @@ -505,19 +449,6 @@ export const useArbTokenBridge = ( txLifecycle.onTxConfirm(receipt) } - const [l1ToL2Msg] = await receipt.getL1ToL2Messages(l2.provider) - if (!l1ToL2Msg) { - return - } - - const l1ToL2MsgData: L1ToL2MessageData = { - fetchingUpdate: false, - status: L1ToL2MessageStatus.NOT_YET_CREATED, // we know its not yet created, we just initiated it - retryableCreationTxID: l1ToL2Msg.retryableCreationId, - l2TxID: undefined - } - - updateTransaction(receipt, tx, l1ToL2MsgData) updateTokenData(erc20L1Address) updateEthBalances() @@ -608,42 +539,6 @@ export const useArbTokenBridge = ( txLifecycle.onTxConfirm(receipt) } - updateTransaction(receipt, tx) - - const l2ToL1Events = receipt.getL2ToL1Events() - - if (l2ToL1Events.length === 1) { - const l2ToL1EventDataResult = l2ToL1Events[0] - - if (!l2ToL1EventDataResult) { - return - } - - const id = getUniqueIdOrHashFromEvent(l2ToL1EventDataResult).toString() - const outgoingMessageState = OutgoingMessageState.UNCONFIRMED - const l2ToL1EventDataResultPlus: L2ToL1EventResultPlus = { - ...l2ToL1EventDataResult, - sender: walletAddress, - destinationAddress: destinationAddress ?? walletAddress, - type: AssetType.ERC20, - tokenAddress: erc20L1Address, - value: amount, - outgoingMessageState, - symbol: symbol, - decimals: decimals, - nodeBlockDeadline: NodeBlockDeadlineStatusTypes.NODE_NOT_CREATED, - l2TxHash: tx.hash, - parentChainId: Number(l1NetworkID), - childChainId: Number(l2NetworkID) - } - - setPendingWithdrawalMap(oldPendingWithdrawalsMap => { - return { - ...oldPendingWithdrawalsMap, - [id]: l2ToL1EventDataResultPlus - } - }) - } updateTokenData(erc20L1Address) return receipt } catch (error) { diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index f55cba22da..5db549441b 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -7,6 +7,7 @@ import { ChainId } from '../util/networks' import { fetchWithdrawals } from '../util/withdrawals/fetchWithdrawals' import { fetchDeposits } from '../util/deposits/fetchDeposits' import { + AssetType, L2ToL1EventResultPlus, WithdrawalInitiated } from './arbTokenBridge.types' @@ -30,7 +31,8 @@ import { useCctpFetching } from '../state/cctpState' import { getProvider, getUpdatedEthDeposit, - getUpdatedEthWithdrawal, + getUpdatedTokenDeposit, + getUpdatedWithdrawal, isSameTransaction, isTxPending } from '../components/TransactionHistory/helpers' @@ -487,12 +489,19 @@ export const useTransactionHistory = ( return } + if (tx.isCctp) { + // todo + return + } + if (tx.isWithdrawal) { - const updatedEthWithdrawal = await getUpdatedEthWithdrawal(tx) - updateCachedTransaction(updatedEthWithdrawal) + const updatedWithdrawal = await getUpdatedWithdrawal(tx) + updateCachedTransaction(updatedWithdrawal) } else { - const updatedEthDeposit = await getUpdatedEthDeposit(tx) - updateCachedTransaction(updatedEthDeposit) + const updatedDeposit = await (tx.assetType === AssetType.ETH + ? getUpdatedEthDeposit(tx) + : getUpdatedTokenDeposit(tx)) + updateCachedTransaction(updatedDeposit) } }, [transactions, updateCachedTransaction] From 785ce9537e90ac9a307c4794036069fb393fee0e Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 8 Dec 2023 19:41:54 +0100 Subject: [PATCH 27/72] swr infinite --- .../src/hooks/useTransactionHistory.ts | 114 ++++++++++-------- 1 file changed, 64 insertions(+), 50 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index e64998a85d..1345e32c8a 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -1,4 +1,5 @@ import useSWRImmutable from 'swr/immutable' +import useSWRInfinite from 'swr/infinite' import { useCallback, useEffect, useMemo, useState } from 'react' import dayjs from 'dayjs' @@ -345,27 +346,40 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { // Pause fetching after specified amount of days. User can resume fetching to get another batch. const PAUSE_SIZE_DAYS = 30 - const [transactions, setTransactions] = useState([]) - const [page, setPage] = useState(0) const [, setPauseCount] = useState(0) const [fetching, setFetching] = useState(true) const { data, loading, error } = useTransactionHistoryWithoutStatuses(address) - const { data: mapData, error: mapError } = useSWRImmutable( - address && !loading && fetching - ? ['complete_tx_list', address, page, data] - : null, - ([, , _page, _data]) => { - const startIndex = _page * MAX_BATCH_SIZE - const endIndex = startIndex + MAX_BATCH_SIZE + const getCacheKey = useCallback( + (pageNumber: number, prevPagesTxs: MergedTransaction[]) => { + if (prevPagesTxs && prevPagesTxs.length === 0) { + console.log('here') + return null + } - return Promise.all( - _data.slice(startIndex, endIndex).map(transformTransaction) - ) - } + return address && !loading + ? (['complete_tx_list', address, pageNumber, data] as const) + : null + }, + [address, loading, data] ) + const { + data: txPages, + error: mapError, + setSize: setPage + } = useSWRInfinite(getCacheKey, ([, , _page, _data]) => { + const startIndex = _page * MAX_BATCH_SIZE + const endIndex = startIndex + MAX_BATCH_SIZE + + return Promise.all( + _data.slice(startIndex, endIndex).map(transformTransaction) + ) + }) + + const transactions: MergedTransaction[] = (txPages || []).flat() + const latestTransactionDaysFromNow = useMemo(() => { return dayjs().diff( dayjs(transactions[transactions.length - 1]?.createdAt), @@ -382,47 +396,47 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { } useEffect(() => { - if (mapData) { - setTransactions(prevTransactions => { - const newTransactions = mapData.filter(Boolean) as MergedTransaction[] - return [...prevTransactions, ...newTransactions] - }) + if (!txPages) { + return + } - // we use setter to access the previous value to get accurate reading - // we will also increment it if fetching gets paused - setPauseCount(prevPauseCount => { - // get the latest transaction, we will use its timestamp to see if we went past maximum number of fetched days - const latestTransaction = mapData[mapData.length - 1] - // max timestamp is our threshold where we stop fetching transactions for this iteration - // it gets incremented by PAUSE_SIZE_DAYS with each iteration - const maxTimestamp = dayjs() - .subtract(PAUSE_SIZE_DAYS * (prevPauseCount + 1), 'days') - .valueOf() - - // check if our latest transaction is past our lookup threshold - if ( - latestTransaction && - latestTransaction.createdAt && - latestTransaction.createdAt <= maxTimestamp - ) { - // if yes, we pause - // we also increment pause count so the next iteration can calculate max timestamp - pause() - return prevPauseCount + 1 - } - - // otherwise do not pause, we also keep the pause count as-is - return prevPauseCount - }) + const lastTxPage = txPages[txPages.length - 1] + + if (!lastTxPage) { + return + } - // mapped data is a full page, so we need to fetch more pages - if (mapData.length === MAX_BATCH_SIZE) { - setPage(prevPage => prevPage + 1) - } else { - setFetching(false) + // we use setter to access the previous value to get accurate reading + // we will also increment it if fetching gets paused + setPauseCount(prevPauseCount => { + // max timestamp is our threshold where we stop fetching transactions for this iteration + // it gets incremented by PAUSE_SIZE_DAYS with each iteration + const maxTimestamp = dayjs() + .subtract(PAUSE_SIZE_DAYS * (prevPauseCount + 1), 'days') + .valueOf() + + // get the latest transaction, we will use its timestamp to see if we went past maximum number of fetched days + const lastTx = lastTxPage[lastTxPage.length - 1] + + // check if our latest transaction is past our lookup threshold + if (lastTx && lastTx.createdAt && lastTx.createdAt <= maxTimestamp) { + // if yes, we pause + // we also increment pause count so the next iteration can calculate max timestamp + pause() + return prevPauseCount + 1 } + + // otherwise do not pause, we also keep the pause count as-is + return prevPauseCount + }) + + // mapped data is a full page, so we need to fetch more pages + if (lastTxPage.length === MAX_BATCH_SIZE) { + setPage(prevPage => prevPage + 1) + } else { + setFetching(false) } - }, [mapData]) + }, [txPages]) if (loading || error) { return { From 7df135633337d4c699e0ae871a3b30aa8c0877b9 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 8 Dec 2023 19:42:39 +0100 Subject: [PATCH 28/72] naming --- .../arb-token-bridge-ui/src/hooks/useTransactionHistory.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 1345e32c8a..9e9a00af96 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -354,7 +354,7 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { const getCacheKey = useCallback( (pageNumber: number, prevPagesTxs: MergedTransaction[]) => { if (prevPagesTxs && prevPagesTxs.length === 0) { - console.log('here') + // no more pages return null } @@ -367,7 +367,7 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { const { data: txPages, - error: mapError, + error: txPagesError, setSize: setPage } = useSWRInfinite(getCacheKey, ([, , _page, _data]) => { const startIndex = _page * MAX_BATCH_SIZE @@ -457,7 +457,7 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { }, loading: fetching, completed: transactions.length === data.length, - error: mapError ?? error, + error: txPagesError ?? error, pause, resume } From c44f665ab0f02eaca8f4dd82d6143d583c1c8cde Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 8 Dec 2023 19:45:48 +0100 Subject: [PATCH 29/72] clean up --- .../arb-token-bridge-ui/src/hooks/useTransactionHistory.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 9e9a00af96..ae8694a5ab 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -352,8 +352,8 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { const { data, loading, error } = useTransactionHistoryWithoutStatuses(address) const getCacheKey = useCallback( - (pageNumber: number, prevPagesTxs: MergedTransaction[]) => { - if (prevPagesTxs && prevPagesTxs.length === 0) { + (pageNumber: number, prevPageTxs: MergedTransaction[]) => { + if (prevPageTxs && prevPageTxs.length === 0) { // no more pages return null } From 95bb24db3935e306c71ee1060ae8478ba23bbd2e Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 11 Dec 2023 11:44:06 +0100 Subject: [PATCH 30/72] fix --- .../src/hooks/useTransactionHistory.ts | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index ae8694a5ab..8917330970 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -345,6 +345,8 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { const MAX_BATCH_SIZE = 10 // Pause fetching after specified amount of days. User can resume fetching to get another batch. const PAUSE_SIZE_DAYS = 30 + // Also pause fetching after specified number of transactions. Just in case user made a lot in the recent days and it's good for local env. + const PAUSE_SIZE_TX_COUNT = 30 const [, setPauseCount] = useState(0) const [fetching, setFetching] = useState(true) @@ -359,22 +361,22 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { } return address && !loading - ? (['complete_tx_list', address, pageNumber, data] as const) + ? (['complete_tx_list', address, pageNumber] as const) : null }, - [address, loading, data] + [address, loading] ) const { data: txPages, error: txPagesError, setSize: setPage - } = useSWRInfinite(getCacheKey, ([, , _page, _data]) => { + } = useSWRInfinite(getCacheKey, ([, , _page]) => { const startIndex = _page * MAX_BATCH_SIZE const endIndex = startIndex + MAX_BATCH_SIZE return Promise.all( - _data.slice(startIndex, endIndex).map(transformTransaction) + data.slice(startIndex, endIndex).map(transformTransaction) ) }) @@ -396,7 +398,7 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { } useEffect(() => { - if (!txPages) { + if (!txPages || !fetching) { return } @@ -417,9 +419,15 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { // get the latest transaction, we will use its timestamp to see if we went past maximum number of fetched days const lastTx = lastTxPage[lastTxPage.length - 1] + const isPastDaysThreshold = + lastTx && lastTx.createdAt && lastTx.createdAt <= maxTimestamp + + // also check if we fetched enough txs to stop + const maxTxCount = PAUSE_SIZE_TX_COUNT * (prevPauseCount + 1) + const isPastTxCountThreshold = txPages.flat().length >= maxTxCount // check if our latest transaction is past our lookup threshold - if (lastTx && lastTx.createdAt && lastTx.createdAt <= maxTimestamp) { + if (isPastDaysThreshold || isPastTxCountThreshold) { // if yes, we pause // we also increment pause count so the next iteration can calculate max timestamp pause() @@ -436,7 +444,7 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { } else { setFetching(false) } - }, [txPages]) + }, [txPages, fetching]) if (loading || error) { return { From 03047bb68d933158e0d616ca2c2bf9abda85784c Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 11 Dec 2023 13:06:47 +0100 Subject: [PATCH 31/72] fixes after swr infinite --- .../TransactionsTableClaimableRow.tsx | 35 ++----------- .../TransactionsTableDepositRow.tsx | 7 ++- .../src/hooks/useArbTokenBridge.ts | 2 +- .../src/hooks/useTransactionHistory.ts | 49 ++++++++++--------- 4 files changed, 35 insertions(+), 58 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx index 06f041c72e..f22a4b0cec 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx @@ -1,4 +1,4 @@ -import { useCallback, useMemo } from 'react' +import { useMemo } from 'react' import { useAccount } from 'wagmi' import { twMerge } from 'tailwind-merge' @@ -305,17 +305,9 @@ export function TransactionsTableClaimableRow({ }) { const isError = tx.status === 'Failure' const sourceChainId = tx.cctpData?.sourceChainId ?? ChainId.ArbitrumOne - const { - isEthereumMainnetOrTestnet: isSourceChainIdEthereum, - isArbitrum: isSourceChainIdArbitrum - } = isNetwork(sourceChainId) + const { isArbitrum: isSourceChainIdArbitrum } = isNetwork(sourceChainId) const { address } = useAccount() - const tokensFromLists = useTokensFromLists({ - parentChainId: tx.parentChainId, - chainId: tx.childChainId - }) - const bgClassName = useMemo(() => { if (isError) return 'bg-brick' if (isPending(tx)) return 'bg-orange' @@ -326,17 +318,9 @@ export function TransactionsTableClaimableRow({ () => sanitizeTokenSymbol(tx.asset, { erc20L1Address: tx.tokenAddress, - chain: getWagmiChain( - isSourceChainIdEthereum ? tx.parentChainId : tx.childChainId - ) + chain: getWagmiChain(tx.parentChainId) }), - [ - tx.asset, - tx.tokenAddress, - tx.parentChainId, - tx.childChainId, - isSourceChainIdEthereum - ] + [tx.asset, tx.tokenAddress, tx.parentChainId] ) const customAddressTxPadding = useMemo( @@ -344,17 +328,6 @@ export function TransactionsTableClaimableRow({ [tx] ) - const getTokenLogoURI = useCallback( - (tx: MergedTransaction) => { - if (!tx.tokenAddress) { - return 'https://raw.githubusercontent.com/ethereum/ethereum-org-website/957567c341f3ad91305c60f7d0b71dcaebfff839/src/assets/assets/eth-diamond-black-gray.png' - } - - return tokensFromLists[tx.tokenAddress]?.logoURI - }, - [tokensFromLists] - ) - if (!tx.sender || !address) { return null } diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDepositRow.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDepositRow.tsx index 7ec83b5ae9..3c393a774b 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDepositRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDepositRow.tsx @@ -5,7 +5,6 @@ import { twMerge } from 'tailwind-merge' import { DepositStatus, MergedTransaction } from '../../state/app/state' import { StatusBadge } from '../common/StatusBadge' import { useRedeemRetryable } from '../../hooks/useRedeemRetryable' -import { useNetworksAndSigners } from '../../hooks/useNetworksAndSigners' import { shortenTxHash } from '../../util/CommonUtils' import { DepositCountdown } from '../common/DepositCountdown' import { ExternalLink } from '../common/ExternalLink' @@ -26,6 +25,7 @@ import { TransactionsTableCustomAddressLabel } from './TransactionsTableCustomAd import { TransactionsTableRowAction } from './TransactionsTableRowAction' import { useChainLayers } from '../../hooks/useChainLayers' import { NetworkImage } from '../common/NetworkImage' +import { getWagmiChain } from '../../util/wagmi/getWagmiChain' function DepositRowStatus({ tx }: { tx: MergedTransaction }) { const { parentLayer, layer } = useChainLayers() @@ -222,7 +222,6 @@ export function TransactionsTableDepositRow({ tx: MergedTransaction className?: string }) { - const { l1 } = useNetworksAndSigners() const { address } = useAccount() const { redeem, isRedeeming } = useRedeemRetryable() const isConnectedToArbitrum = useIsConnectedToArbitrum() @@ -272,9 +271,9 @@ export function TransactionsTableDepositRow({ () => sanitizeTokenSymbol(tx.asset, { erc20L1Address: tx.tokenAddress, - chain: l1.network + chain: getWagmiChain(tx.parentChainId) }), - [l1.network, tx.asset, tx.tokenAddress] + [tx.parentChainId, tx.asset, tx.tokenAddress] ) const customAddressTxPadding = useMemo( diff --git a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts index 22c4ca6ed1..126b64f84f 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts @@ -597,7 +597,7 @@ export const useArbTokenBridge = ( uniqueId: null, isWithdrawal: true, blockNum: null, - tokenAddress: null, + tokenAddress: erc20L1Address, parentChainId: Number(l1NetworkID), childChainId: Number(l2NetworkID) }) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index b73e39fd54..67adbf5fa9 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -1,4 +1,3 @@ -import { mutate, useSWRConfig, unstable_serialize } from 'swr' import useSWRImmutable from 'swr/immutable' import useSWRInfinite from 'swr/infinite' import { useCallback, useEffect, useMemo, useState } from 'react' @@ -369,7 +368,6 @@ export const useTransactionHistory = ( const [fetching, setFetching] = useState(true) const { data, loading, error } = useTransactionHistoryWithoutStatuses(address) - const { cache } = useSWRConfig() const getCacheKey = useCallback( (pageNumber: number, prevPageTxs: MergedTransaction[] | undefined) => { @@ -388,15 +386,24 @@ export const useTransactionHistory = ( const { data: txPages, error: txPagesError, - setSize: setPage - } = useSWRInfinite(getCacheKey, ([, , _page]) => { - const startIndex = _page * MAX_BATCH_SIZE - const endIndex = startIndex + MAX_BATCH_SIZE + setSize: setPage, + mutate + } = useSWRInfinite( + getCacheKey, + ([, , _page]) => { + const startIndex = _page * MAX_BATCH_SIZE + const endIndex = startIndex + MAX_BATCH_SIZE - return Promise.all( - data.slice(startIndex, endIndex).map(transformTransaction) - ) - }) + return Promise.all( + data.slice(startIndex, endIndex).map(transformTransaction) + ) + }, + { + revalidateIfStale: false, + revalidateOnFocus: false, + revalidateOnReconnect: false + } + ) const transactions: MergedTransaction[] = (txPages || []).flat() @@ -417,19 +424,17 @@ export const useTransactionHistory = ( const addPendingTransaction = useCallback( (tx: MergedTransaction) => { - const cacheKey = getCacheKey(0, undefined) - - if (!cacheKey || !isTxPending(tx)) { - return - } - - const prevTransactions = - (cache.get(unstable_serialize(cacheKey)) - ?.data as MergedTransaction[]) || [] - - mutate(cacheKey, [tx, ...prevTransactions], true) + mutate(pages => { + if (!pages) { + // e.g. when tx history is still fetching + return [[tx]] + } + + // add the new tx at the start of the first page + return [[tx, ...(pages[0] ?? [])], ...pages.slice(1)] + }, false) }, - [cache, getCacheKey] + [mutate] ) useEffect(() => { From 71a5416d80b5913d0f102eb243b0ae68bf9d9c12 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 11 Dec 2023 13:54:30 +0100 Subject: [PATCH 32/72] fixes --- .../src/hooks/useTransactionHistory.ts | 59 ++++++++++--------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index ed4ddd9154..5abb04400f 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -423,17 +423,6 @@ export const useTransactionHistory = ( setFetching(true) } - const getTransactionsForPage = useCallback( - (pageNumber: number) => { - const cacheKey = getCacheKey(pageNumber) - return ( - (cache.get(unstable_serialize(cacheKey)) - ?.data as MergedTransaction[]) || [] - ) - }, - [cache, getCacheKey] - ) - const addPendingTransaction = useCallback( (tx: MergedTransaction) => { mutate(pages => { @@ -451,31 +440,47 @@ export const useTransactionHistory = ( const updateCachedTransaction = useCallback( (newTx: MergedTransaction) => { - for (let i = 0; i < page; i++) { - const oldTxsForPage = getTransactionsForPage(i) - // see if tx exist in cache for this page - const foundInCache = !!oldTxsForPage.find(oldTx => + if (!txPages) { + return + } + + let pageNumberToUpdate = 0 + + // search cache for the tx to update + while ( + !txPages[pageNumberToUpdate]?.find(oldTx => isSameTransaction(oldTx, newTx) ) + ) { + pageNumberToUpdate++ - if (!foundInCache) { - // tx does not exist + if (pageNumberToUpdate > txPages.length) { + // tx not found return } + } - // if exists, then we replace it with the new transaction - const newTxs = oldTxsForPage.map(oldTx => { - if (isSameTransaction(oldTx, newTx)) { - return newTx - } - return oldTx - }) + const oldPageToUpdate = txPages[pageNumberToUpdate] - mutate(getCacheKey(i), newTxs, false) - break + if (!oldPageToUpdate) { + return } + + // replace the old tx with the new tx + const updatedPage = oldPageToUpdate.map(oldTx => { + return isSameTransaction(oldTx, newTx) ? newTx : oldTx + }) + + // all old pages including the new updated page + const newTxPages = [ + ...[...txPages].slice(0, pageNumberToUpdate), + updatedPage, + ...[...txPages].slice(pageNumberToUpdate + 1) + ] + + mutate(newTxPages) }, - [getCacheKey, getTransactionsForPage, page] + [mutate, txPages] ) const updatePendingTransaction = useCallback( From 6b13f3683e5e68d80de24e4db1ec3a1303f5571d Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 11 Dec 2023 16:28:14 +0100 Subject: [PATCH 33/72] fix tx list --- .../src/hooks/useTransactionHistory.ts | 135 +++++------------- 1 file changed, 32 insertions(+), 103 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 8917330970..264fcbceb1 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -30,8 +30,6 @@ import { FetchWithdrawalsFromSubgraphResult } from '../util/withdrawals/fetchWit import { updateAdditionalDepositData } from '../util/deposits/helpers' import { useCctpFetching } from '../state/cctpState' -const PAGE_SIZE = 100 - export type Deposit = Transaction export type Withdrawal = @@ -171,15 +169,6 @@ function getProvider(chainId: ChainId) { const useTransactionHistoryWithoutStatuses = ( address: `0x${string}` | undefined ) => { - const [deposits, setDeposits] = useState([]) - const [withdrawals, setWithdrawals] = useState([]) - - const [depositsPage, setDepositsPage] = useState(0) - const [withdrawalsPage, setWithdrawalsPage] = useState(0) - - const [depositsLoading, setDepositsLoading] = useState(true) - const [withdrawalsLoading, setWithdrawalsLoading] = useState(true) - const cctpTransfersMainnet = useCctpFetching({ walletAddress: address, l1ChainId: ChainId.Ethereum, @@ -216,113 +205,52 @@ const useTransactionHistoryWithoutStatuses = ( cctpTransfersTestnet.isLoadingDeposits || cctpTransfersTestnet.isLoadingWithdrawals - const shouldFetchNextPageForChainPair = useCallback( - (chainPairIndex: number, direction: 'deposits' | 'withdrawals') => { - const isDeposits = direction === 'deposits' - const page = isDeposits ? depositsPage : withdrawalsPage - const transactions = isDeposits ? deposits : withdrawals - - if (page === 0) { - return true - } - - const txCountForChainPair = transactions[chainPairIndex]?.length ?? 0 - // fetch next page for a chain pair if all fetched pages are full - // we check for >= in case some txs were included by initiating a transfer - return txCountForChainPair / page >= PAGE_SIZE - }, - [depositsPage, withdrawalsPage, deposits, withdrawals] - ) - - const { data: depositsData, error: depositsError } = useSWRImmutable( - address ? ['tx_list', 'deposits', address, depositsPage] : null, - ([, , _address, _page]) => { + const { + data: depositsData, + error: depositsError, + isLoading: depositsLoading + } = useSWRImmutable( + address ? ['tx_list', 'deposits', address] : null, + ([, , _address]) => { return Promise.all( - multiChainFetchList.map((chainPair, chainPairIndex) => { - if (!shouldFetchNextPageForChainPair(chainPairIndex, 'deposits')) { - return [] - } - + multiChainFetchList.map(chainPair => { return fetchDeposits({ sender: _address, receiver: _address, l1Provider: getProvider(chainPair.parentChain), l2Provider: getProvider(chainPair.chain), - pageNumber: _page, - pageSize: PAGE_SIZE + pageNumber: 0, + pageSize: 1000 }) }) ) } ) - const { data: withdrawalsData, error: withdrawalsError } = useSWRImmutable( - address ? ['tx_list', 'withdrawals', address, withdrawalsPage] : null, - ([, , _address, _page]) => { + const { + data: withdrawalsData, + error: withdrawalsError, + isLoading: withdrawalsLoading + } = useSWRImmutable( + address ? ['tx_list', 'withdrawals', address] : null, + ([, , _address]) => { return Promise.all( - multiChainFetchList.map((chainPair, chainPairIndex) => { - if (!shouldFetchNextPageForChainPair(chainPairIndex, 'withdrawals')) { - return [] - } - + multiChainFetchList.map(chainPair => { return fetchWithdrawals({ sender: _address, receiver: _address, l1Provider: getProvider(chainPair.parentChain), l2Provider: getProvider(chainPair.chain), - pageNumber: _page, - pageSize: PAGE_SIZE + pageNumber: 0, + pageSize: 1000 }) }) ) } ) - function addDeposits(transactions: Deposit[][]) { - const fetchedSomeData = transactions.some(item => item.length > 0) - - if (fetchedSomeData) { - setDeposits(prevDeposits => { - return transactions.map((transactionsForChainPair, chainPairIndex) => [ - ...(prevDeposits[chainPairIndex] ?? []), - ...(transactionsForChainPair ?? []) - ]) - }) - setDepositsPage(prevPage => prevPage + 1) - } else { - setDepositsLoading(false) - } - } - - function addWithdrawals(transactions: Withdrawal[][]) { - const fetchedSomeData = transactions.some(item => item.length > 0) - - if (fetchedSomeData) { - setWithdrawals(prevWithdrawal => { - return transactions.map((transactionsForChainPair, chainPairIndex) => [ - ...(prevWithdrawal[chainPairIndex] ?? []), - ...(transactionsForChainPair ?? []) - ]) - }) - setWithdrawalsPage(prevPage => prevPage + 1) - } else { - setWithdrawalsLoading(false) - } - } - - useEffect(() => { - if (!depositsData) { - return - } - addDeposits(depositsData) - }, [depositsData]) - - useEffect(() => { - if (!withdrawalsData) { - return - } - addWithdrawals(withdrawalsData) - }, [withdrawalsData]) + const deposits = (depositsData || []).flat() + const withdrawals = (withdrawalsData || []).flat() // merge deposits and withdrawals and sort them by date const transactions = [...deposits, ...withdrawals, ...combinedCctpTransfers] @@ -395,10 +323,11 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { function resume() { setFetching(true) + setPage(prevPage => prevPage + 1) } useEffect(() => { - if (!txPages || !fetching) { + if (!txPages) { return } @@ -432,19 +361,19 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { // we also increment pause count so the next iteration can calculate max timestamp pause() return prevPauseCount + 1 + } else { + // mapped data is a full page, so we need to fetch more pages + if (lastTxPage.length === MAX_BATCH_SIZE) { + setPage(prevPage => prevPage + 1) + } else { + setFetching(false) + } } // otherwise do not pause, we also keep the pause count as-is return prevPauseCount }) - - // mapped data is a full page, so we need to fetch more pages - if (lastTxPage.length === MAX_BATCH_SIZE) { - setPage(prevPage => prevPage + 1) - } else { - setFetching(false) - } - }, [txPages, fetching]) + }, [txPages]) if (loading || error) { return { From 6070b515e0dfb8f65a1c321e92affbdb305036d2 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 11 Dec 2023 18:46:22 +0100 Subject: [PATCH 34/72] fix and clean up --- .../src/components/App/App.tsx | 2 - .../components/MainContent/MainContent.tsx | 6 +- .../TransactionHistory/TransactionHistory.tsx | 18 +-- .../syncers/PendingTransactionsUpdater.tsx | 75 ---------- .../src/hooks/useTransactionHistory.ts | 132 +++++++++--------- 5 files changed, 83 insertions(+), 150 deletions(-) delete mode 100644 packages/arb-token-bridge-ui/src/components/syncers/PendingTransactionsUpdater.tsx diff --git a/packages/arb-token-bridge-ui/src/components/App/App.tsx b/packages/arb-token-bridge-ui/src/components/App/App.tsx index c8d50ebe0a..ba0511243c 100644 --- a/packages/arb-token-bridge-ui/src/components/App/App.tsx +++ b/packages/arb-token-bridge-ui/src/components/App/App.tsx @@ -18,7 +18,6 @@ import { Alert } from '../common/Alert' import { MainContent } from '../MainContent/MainContent' import { ArbTokenBridgeStoreSync } from '../syncers/ArbTokenBridgeStoreSync' import { BalanceUpdater } from '../syncers/BalanceUpdater' -import { PendingTransactionsUpdater } from '../syncers/PendingTransactionsUpdater' import { RetryableTxnsIncluder } from '../syncers/RetryableTxnsIncluder' import { TokenListSyncer } from '../syncers/TokenListSyncer' import { useDialog } from '../common/Dialog' @@ -120,7 +119,6 @@ const AppContent = (): JSX.Element => { - diff --git a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx index f086db0651..d8d4c9e49e 100644 --- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx +++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx @@ -7,6 +7,8 @@ import { useAppContextActions, useAppContextState } from '../App/AppContext' import { ArbitrumStats, statsLocalStorageKey } from './ArbitrumStats' import { SettingsDialog } from '../common/SettingsDialog' import { TransactionHistory } from '../TransactionHistory/TransactionHistory' +import { useTransactionHistory } from '../../hooks/useTransactionHistory' +import { useAccount } from 'wagmi' export const motionDivProps = { layout: true, @@ -26,6 +28,8 @@ export const motionDivProps = { export function MainContent() { const { closeTransactionHistoryPanel } = useAppContextActions() + const { address } = useAccount() + const transactionHistoryProps = useTransactionHistory(address) const { layout: { isTransactionHistoryPanelVisible } } = useAppContextState() @@ -54,7 +58,7 @@ export function MainContent() { onClose={closeTransactionHistoryPanel} scrollable={false} > - + {/* Settings panel */} diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx index f8a8f161f3..4e95eadada 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx @@ -1,9 +1,8 @@ import { useMemo } from 'react' import { Tab } from '@headlessui/react' import { twMerge } from 'tailwind-merge' -import { useAccount } from 'wagmi' -import { useTransactionHistory } from '../../hooks/useTransactionHistory' +import { TransactionHistoryParams } from '../../hooks/useTransactionHistory' import { TransactionHistoryTable } from './TransactionHistoryTable' import { isTxClaimable, @@ -18,16 +17,19 @@ import { TabButton } from '../common/Tab' const roundedTabClasses = 'roundedTab ui-not-selected:arb-hover relative flex flex-row flex-nowrap items-center gap-0.5 md:gap-2 rounded-tl-lg rounded-tr-lg px-2 md:px-4 py-2 text-base ui-selected:bg-white ui-not-selected:text-white justify-center md:justify-start grow md:grow-0' -export const TransactionHistory = () => { - const { address } = useAccount() - +export const TransactionHistory = ({ + props +}: { + props: TransactionHistoryParams & { address: `0x${string}` | undefined } +}) => { const { data: { transactions, numberOfDays }, + address, loading, completed, - resume, - error - } = useTransactionHistory(address) + error, + resume + } = props const groupedTransactions = useMemo( () => diff --git a/packages/arb-token-bridge-ui/src/components/syncers/PendingTransactionsUpdater.tsx b/packages/arb-token-bridge-ui/src/components/syncers/PendingTransactionsUpdater.tsx deleted file mode 100644 index 7005389ad5..0000000000 --- a/packages/arb-token-bridge-ui/src/components/syncers/PendingTransactionsUpdater.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { useCallback, useEffect } from 'react' -import { TransactionReceipt } from '@ethersproject/providers' -import { useAccount } from 'wagmi' - -import { Transaction, txnTypeToLayer } from '../../hooks/useTransactions' -import { useActions, useAppState } from '../../state' -import { useInterval } from '../common/Hooks' -import { useNetworksAndSigners } from '../../hooks/useNetworksAndSigners' -import { useCctpState, useUpdateCctpTransactions } from '../../state/cctpState' - -export function PendingTransactionsUpdater(): JSX.Element { - const actions = useActions() - const { - l1: { provider: l1Provider, network: l1Network }, - l2: { provider: l2Provider, network: l2Network } - } = useNetworksAndSigners() - const { updateCctpTransactions } = useUpdateCctpTransactions() - - const { - app: { arbTokenBridge, arbTokenBridgeLoaded } - } = useAppState() - const { address } = useAccount() - const { resetTransfers } = useCctpState() - - useEffect(() => { - resetTransfers() - }, [address, l1Network.id, l2Network.id, resetTransfers]) - - const getTransactionReceipt = useCallback( - (tx: Transaction) => { - const provider = txnTypeToLayer(tx.type) === 2 ? l2Provider : l1Provider - return provider.getTransactionReceipt(tx.txID) - }, - [l1Provider, l2Provider] - ) - - // eslint-disable-next-line consistent-return - const checkAndUpdatePendingTransactions = useCallback(() => { - if (!arbTokenBridgeLoaded) return - updateCctpTransactions() - const pendingTransactions = actions.app.getPendingTransactions() - if (pendingTransactions.length) { - console.info( - `Checking and updating ${pendingTransactions.length} pending transactions' statuses` - ) - - // eslint-disable-next-line consistent-return - return Promise.all( - pendingTransactions.map((tx: Transaction) => getTransactionReceipt(tx)) - ).then((txReceipts: (TransactionReceipt | null)[]) => { - txReceipts.forEach((txReceipt: TransactionReceipt | null, i) => { - if (!txReceipt) { - console.info( - 'Transaction receipt not yet found:', - pendingTransactions[i]?.txID - ) - } else { - arbTokenBridge?.transactions?.updateTransaction(txReceipt) - } - }) - }) - } - }, [getTransactionReceipt, arbTokenBridge, arbTokenBridgeLoaded]) - const { forceTrigger: forceTriggerUpdate } = useInterval( - checkAndUpdatePendingTransactions, - 4000 - ) - - useEffect(() => { - // force trigger update each time loaded change happens - forceTriggerUpdate() - }, [arbTokenBridgeLoaded]) - - return <> -} diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 264fcbceb1..64e8d58c04 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -30,6 +30,19 @@ import { FetchWithdrawalsFromSubgraphResult } from '../util/withdrawals/fetchWit import { updateAdditionalDepositData } from '../util/deposits/helpers' import { useCctpFetching } from '../state/cctpState' +export type TransactionHistoryParams = { + data: { + transactions: MergedTransaction[] + numberOfDays: number + } + loading: boolean + completed: boolean + error: unknown + pause: () => void + resume: () => void + addPendingTransaction?: (tx: MergedTransaction) => void +} + export type Deposit = Transaction export type Withdrawal = @@ -268,15 +281,14 @@ const useTransactionHistoryWithoutStatuses = ( * Maps additional info to previously fetches transaction history, starting with the earliest data. * This is done in small batches to safely meet RPC limits. */ -export const useTransactionHistory = (address: `0x${string}` | undefined) => { +export const useTransactionHistory = ( + address: `0x${string}` | undefined +): TransactionHistoryParams => { // max number of transactions mapped in parallel const MAX_BATCH_SIZE = 10 - // Pause fetching after specified amount of days. User can resume fetching to get another batch. - const PAUSE_SIZE_DAYS = 30 - // Also pause fetching after specified number of transactions. Just in case user made a lot in the recent days and it's good for local env. - const PAUSE_SIZE_TX_COUNT = 30 + // Pause fetching after specified number of pages. User can resume fetching to get another batch. + const PAUSE_SIZE_PAGE_COUNT = 2 - const [, setPauseCount] = useState(0) const [fetching, setFetching] = useState(true) const { data, loading, error } = useTransactionHistoryWithoutStatuses(address) @@ -299,14 +311,56 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { data: txPages, error: txPagesError, setSize: setPage - } = useSWRInfinite(getCacheKey, ([, , _page]) => { - const startIndex = _page * MAX_BATCH_SIZE - const endIndex = startIndex + MAX_BATCH_SIZE + } = useSWRInfinite( + getCacheKey, + ([, , _page]) => { + const startIndex = _page * MAX_BATCH_SIZE + const endIndex = startIndex + MAX_BATCH_SIZE - return Promise.all( - data.slice(startIndex, endIndex).map(transformTransaction) - ) - }) + return Promise.all( + data.slice(startIndex, endIndex).map(transformTransaction) + ) + }, + { + revalidateOnFocus: false, + revalidateOnReconnect: false, + revalidateIfStale: false, + shouldRetryOnError: false, + refreshWhenOffline: false, + refreshWhenHidden: false, + revalidateFirstPage: false, + refreshInterval: 0 + } + ) + + useEffect(() => { + if (!txPages) { + return + } + + const lastPage = txPages[txPages.length - 1] + + if (!lastPage) { + return + } + + const shouldFetchNextPage = lastPage.length === MAX_BATCH_SIZE + + if (!shouldFetchNextPage) { + setFetching(false) + return + } + + const shouldPause = + txPages.length > 0 && txPages.length % PAUSE_SIZE_PAGE_COUNT === 0 + + if (shouldPause) { + pause() + return + } + + setPage(p => p + 1) + }, [txPages]) const transactions: MergedTransaction[] = (txPages || []).flat() @@ -326,58 +380,9 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { setPage(prevPage => prevPage + 1) } - useEffect(() => { - if (!txPages) { - return - } - - const lastTxPage = txPages[txPages.length - 1] - - if (!lastTxPage) { - return - } - - // we use setter to access the previous value to get accurate reading - // we will also increment it if fetching gets paused - setPauseCount(prevPauseCount => { - // max timestamp is our threshold where we stop fetching transactions for this iteration - // it gets incremented by PAUSE_SIZE_DAYS with each iteration - const maxTimestamp = dayjs() - .subtract(PAUSE_SIZE_DAYS * (prevPauseCount + 1), 'days') - .valueOf() - - // get the latest transaction, we will use its timestamp to see if we went past maximum number of fetched days - const lastTx = lastTxPage[lastTxPage.length - 1] - const isPastDaysThreshold = - lastTx && lastTx.createdAt && lastTx.createdAt <= maxTimestamp - - // also check if we fetched enough txs to stop - const maxTxCount = PAUSE_SIZE_TX_COUNT * (prevPauseCount + 1) - const isPastTxCountThreshold = txPages.flat().length >= maxTxCount - - // check if our latest transaction is past our lookup threshold - if (isPastDaysThreshold || isPastTxCountThreshold) { - // if yes, we pause - // we also increment pause count so the next iteration can calculate max timestamp - pause() - return prevPauseCount + 1 - } else { - // mapped data is a full page, so we need to fetch more pages - if (lastTxPage.length === MAX_BATCH_SIZE) { - setPage(prevPage => prevPage + 1) - } else { - setFetching(false) - } - } - - // otherwise do not pause, we also keep the pause count as-is - return prevPauseCount - }) - }, [txPages]) - if (loading || error) { return { - data: { transactions: [], total: undefined, numberOfDays: 0 }, + data: { transactions: [], numberOfDays: 0 }, loading, error, completed: true, @@ -389,7 +394,6 @@ export const useTransactionHistory = (address: `0x${string}` | undefined) => { return { data: { transactions, - total: data.length, numberOfDays: latestTransactionDaysFromNow }, loading: fetching, From 2c6244a110057763771887826e4df3f41f5057fa Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 12 Dec 2023 12:45:51 +0100 Subject: [PATCH 35/72] based on days --- .../src/hooks/useTransactionHistory.ts | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 64e8d58c04..900fc8426a 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -288,6 +288,7 @@ export const useTransactionHistory = ( const MAX_BATCH_SIZE = 10 // Pause fetching after specified number of pages. User can resume fetching to get another batch. const PAUSE_SIZE_PAGE_COUNT = 2 + const PAUSE_SIZE_DAYS = 30 const [fetching, setFetching] = useState(true) @@ -338,12 +339,14 @@ export const useTransactionHistory = ( return } + const firstPage = txPages[0] const lastPage = txPages[txPages.length - 1] - if (!lastPage) { + if (!firstPage || !lastPage) { return } + // if a full page is fetched, we need to fetch more const shouldFetchNextPage = lastPage.length === MAX_BATCH_SIZE if (!shouldFetchNextPage) { @@ -351,10 +354,36 @@ export const useTransactionHistory = ( return } - const shouldPause = + const newestTx = firstPage[0] + const oldestTx = lastPage[lastPage.length - 1] + + if (!newestTx || !oldestTx) { + return + } + + const oldestTxDaysAgo = dayjs().diff(dayjs(oldestTx.createdAt ?? 0), 'days') + + const newestAndOldestTxDayDiff = dayjs(newestTx.createdAt ?? 0).diff( + dayjs(oldestTx.createdAt ?? 0), + 'days' + ) + + // Derive pause count + const estimatedPauseCountBasedOnDays = Math.floor( + newestAndOldestTxDayDiff / PAUSE_SIZE_DAYS + ) + + // Calculate the next pause threshold based on the day difference between transactions + const nextPauseThresholdBasedOnDays = + (estimatedPauseCountBasedOnDays + 1) * PAUSE_SIZE_DAYS + + const shouldPauseBasedOnDays = + oldestTxDaysAgo >= nextPauseThresholdBasedOnDays + + const shouldPauseBasedOnPageCount = txPages.length > 0 && txPages.length % PAUSE_SIZE_PAGE_COUNT === 0 - if (shouldPause) { + if (shouldPauseBasedOnDays || shouldPauseBasedOnPageCount) { pause() return } @@ -364,11 +393,13 @@ export const useTransactionHistory = ( const transactions: MergedTransaction[] = (txPages || []).flat() - const latestTransactionDaysFromNow = useMemo(() => { - return dayjs().diff( + const oldestTransactionDaysAgo = useMemo(() => { + const daysAgo = dayjs().diff( dayjs(transactions[transactions.length - 1]?.createdAt), 'days' ) + // don't show 0 + return Math.min(daysAgo, 1) }, [transactions]) function pause() { @@ -382,7 +413,7 @@ export const useTransactionHistory = ( if (loading || error) { return { - data: { transactions: [], numberOfDays: 0 }, + data: { transactions: [], numberOfDays: 1 }, loading, error, completed: true, @@ -394,7 +425,7 @@ export const useTransactionHistory = ( return { data: { transactions, - numberOfDays: latestTransactionDaysFromNow + numberOfDays: oldestTransactionDaysAgo }, loading: fetching, completed: transactions.length === data.length, From bd9a7c2274224e42f9ac011a66b374fca4dcc72b Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 12 Dec 2023 13:27:01 +0100 Subject: [PATCH 36/72] based on days only --- .../src/hooks/useTransactionHistory.ts | 41 +++++++------------ 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 900fc8426a..f2bb6c0fcd 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -286,11 +286,11 @@ export const useTransactionHistory = ( ): TransactionHistoryParams => { // max number of transactions mapped in parallel const MAX_BATCH_SIZE = 10 - // Pause fetching after specified number of pages. User can resume fetching to get another batch. - const PAUSE_SIZE_PAGE_COUNT = 2 + // Pause fetching after specified number of days. User can resume fetching to get another batch. const PAUSE_SIZE_DAYS = 30 const [fetching, setFetching] = useState(true) + const [pauseCount, setPauseCount] = useState(0) const { data, loading, error } = useTransactionHistoryWithoutStatuses(address) @@ -311,6 +311,7 @@ export const useTransactionHistory = ( const { data: txPages, error: txPagesError, + size: page, setSize: setPage } = useSWRInfinite( getCacheKey, @@ -335,7 +336,11 @@ export const useTransactionHistory = ( ) useEffect(() => { - if (!txPages) { + if (!txPages || !fetching) { + return + } + + if (page > txPages.length) { return } @@ -363,33 +368,17 @@ export const useTransactionHistory = ( const oldestTxDaysAgo = dayjs().diff(dayjs(oldestTx.createdAt ?? 0), 'days') - const newestAndOldestTxDayDiff = dayjs(newestTx.createdAt ?? 0).diff( - dayjs(oldestTx.createdAt ?? 0), - 'days' - ) + const nextPauseThresholdDays = (pauseCount + 1) * PAUSE_SIZE_DAYS + const shouldPause = oldestTxDaysAgo >= nextPauseThresholdDays - // Derive pause count - const estimatedPauseCountBasedOnDays = Math.floor( - newestAndOldestTxDayDiff / PAUSE_SIZE_DAYS - ) - - // Calculate the next pause threshold based on the day difference between transactions - const nextPauseThresholdBasedOnDays = - (estimatedPauseCountBasedOnDays + 1) * PAUSE_SIZE_DAYS - - const shouldPauseBasedOnDays = - oldestTxDaysAgo >= nextPauseThresholdBasedOnDays - - const shouldPauseBasedOnPageCount = - txPages.length > 0 && txPages.length % PAUSE_SIZE_PAGE_COUNT === 0 - - if (shouldPauseBasedOnDays || shouldPauseBasedOnPageCount) { + if (shouldPause) { pause() + setPauseCount(prevPauseCount => prevPauseCount + 1) return } - setPage(p => p + 1) - }, [txPages]) + setPage(prevPage => prevPage + 1) + }, [txPages, setPage, page, pauseCount, fetching]) const transactions: MergedTransaction[] = (txPages || []).flat() @@ -399,7 +388,7 @@ export const useTransactionHistory = ( 'days' ) // don't show 0 - return Math.min(daysAgo, 1) + return Math.max(daysAgo, 1) }, [transactions]) function pause() { From d4fe7b716573c358369add31f7dce91648e3459e Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 12 Dec 2023 14:32:05 +0100 Subject: [PATCH 37/72] fix --- .../arb-token-bridge-ui/src/hooks/useTransactionHistory.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 337f906be0..ec5fc1ce88 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -338,6 +338,8 @@ export const useTransactionHistory = ( } ) + const transactions: MergedTransaction[] = (txPages || []).flat() + const addPendingTransaction = useCallback( (tx: MergedTransaction) => { if (!isTxPending(tx)) { @@ -478,8 +480,6 @@ export const useTransactionHistory = ( setPage(prevPage => prevPage + 1) }, [txPages, setPage, page, pauseCount, fetching]) - const transactions: MergedTransaction[] = (txPages || []).flat() - const oldestTransactionDaysAgo = useMemo(() => { const daysAgo = dayjs().diff( dayjs(transactions[transactions.length - 1]?.createdAt), From e2be948204a023a23bdcdf0e9777b6ac62c65dd3 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 12 Dec 2023 16:20:50 +0100 Subject: [PATCH 38/72] separate swr for new txs --- .../components/MainContent/MainContent.tsx | 2 +- .../src/hooks/useTransactionHistory.ts | 42 +++++++++++-------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx index 6655b2b6c5..2c172ea679 100644 --- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx +++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx @@ -29,7 +29,7 @@ export const motionDivProps = { export function MainContent() { const { address } = useAccount() const { closeTransactionHistoryPanel } = useAppContextActions() - const transactionHistoryProps = useTransactionHistory(address) + const transactionHistoryProps = useTransactionHistory(address, true) const { layout: { isTransactionHistoryPanelVisible } } = useAppContextState() diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index cbce059123..2fa3e86199 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -283,7 +283,8 @@ const useTransactionHistoryWithoutStatuses = ( * This is done in small batches to safely meet RPC limits. */ export const useTransactionHistory = ( - address: `0x${string}` | undefined + address: `0x${string}` | undefined, + runFetcher = false ): TransactionHistoryParams => { // max number of transactions mapped in parallel const MAX_BATCH_SIZE = 10 @@ -313,8 +314,7 @@ export const useTransactionHistory = ( data: txPages, error: txPagesError, size: page, - setSize: setPage, - mutate + setSize: setPage } = useSWRInfinite( getCacheKey, ([, , _page]) => { @@ -337,31 +337,31 @@ export const useTransactionHistory = ( } ) + // transfers initiated by the user during the current session + // we store it separately as there are a lot of side effects when mutating SWRInfinite + const { data: newTransactionsData, mutate } = useSWRImmutable< + MergedTransaction[] + >(address ? ['new_tx_list', address] : null) + const addPendingTransaction = useCallback( (tx: MergedTransaction) => { if (!isTxPending(tx)) { return } - mutate(pages => { - if (!pages) { - // e.g. when tx history is still fetching - return [[tx]] + mutate(currentNewTransactions => { + if (!currentNewTransactions) { + return [tx] } - // add the new tx at the start of the first page - return [[tx, ...(pages[0] ?? [])], ...pages.slice(1)] - }, false) + return [tx, ...currentNewTransactions] + }) }, [mutate] ) useEffect(() => { - if (!txPages || !fetching) { - return - } - - if (page > txPages.length) { + if (!txPages || !fetching || !runFetcher) { return } @@ -398,10 +398,16 @@ export const useTransactionHistory = ( return } - setPage(prevPage => prevPage + 1) - }, [txPages, setPage, page, pauseCount, fetching]) + // make sure we don't over-fetch + if (page === txPages.length) { + setPage(prevPage => prevPage + 1) + } + }, [txPages, setPage, page, pauseCount, fetching, runFetcher]) - const transactions: MergedTransaction[] = (txPages || []).flat() + const transactions: MergedTransaction[] = [ + ...(newTransactionsData || []), + ...(txPages || []) + ].flat() const oldestTransactionDaysAgo = useMemo(() => { const daysAgo = dayjs().diff( From 952bb2e83b125b82a14c1257ac7a5316ca62d697 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 12 Dec 2023 16:25:15 +0100 Subject: [PATCH 39/72] remove unused param --- .../src/components/MainContent/MainContent.tsx | 2 +- .../arb-token-bridge-ui/src/hooks/useTransactionHistory.ts | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx index 2c172ea679..6655b2b6c5 100644 --- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx +++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx @@ -29,7 +29,7 @@ export const motionDivProps = { export function MainContent() { const { address } = useAccount() const { closeTransactionHistoryPanel } = useAppContextActions() - const transactionHistoryProps = useTransactionHistory(address, true) + const transactionHistoryProps = useTransactionHistory(address) const { layout: { isTransactionHistoryPanelVisible } } = useAppContextState() diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 2fa3e86199..bd26bcd4d6 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -283,8 +283,7 @@ const useTransactionHistoryWithoutStatuses = ( * This is done in small batches to safely meet RPC limits. */ export const useTransactionHistory = ( - address: `0x${string}` | undefined, - runFetcher = false + address: `0x${string}` | undefined ): TransactionHistoryParams => { // max number of transactions mapped in parallel const MAX_BATCH_SIZE = 10 @@ -361,7 +360,7 @@ export const useTransactionHistory = ( ) useEffect(() => { - if (!txPages || !fetching || !runFetcher) { + if (!txPages || !fetching) { return } @@ -402,7 +401,7 @@ export const useTransactionHistory = ( if (page === txPages.length) { setPage(prevPage => prevPage + 1) } - }, [txPages, setPage, page, pauseCount, fetching, runFetcher]) + }, [txPages, setPage, page, pauseCount, fetching]) const transactions: MergedTransaction[] = [ ...(newTransactionsData || []), From 7809856f7dcebb759a124faeecbc5568f7e32661 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 12 Dec 2023 16:38:23 +0100 Subject: [PATCH 40/72] address review comments --- .../TransactionHistory/TransactionsTableClaimableRow.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx index f22a4b0cec..b7c1478ea7 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx @@ -29,7 +29,6 @@ import { useRemainingTime } from '../../state/cctpState' import { useChainLayers } from '../../hooks/useChainLayers' import { getWagmiChain } from '../../util/wagmi/getWagmiChain' import { NetworkImage } from '../common/NetworkImage' -import { useTokensFromLists } from '../TransferPanel/TokenSearchUtils' type CommonProps = { tx: MergedTransaction From ceb555b64b9a9652b7d7ffb0f71d8ca65a483e44 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 12 Dec 2023 17:38:35 +0100 Subject: [PATCH 41/72] address comment --- .../TransactionsTableClaimableRow.tsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx index b7c1478ea7..325b03d1df 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx @@ -304,7 +304,10 @@ export function TransactionsTableClaimableRow({ }) { const isError = tx.status === 'Failure' const sourceChainId = tx.cctpData?.sourceChainId ?? ChainId.ArbitrumOne - const { isArbitrum: isSourceChainIdArbitrum } = isNetwork(sourceChainId) + const { + isArbitrum: isSourceChainIdArbitrum, + isEthereumMainnetOrTestnet: isSourceChainIdEthereum + } = isNetwork(sourceChainId) const { address } = useAccount() const bgClassName = useMemo(() => { @@ -317,9 +320,17 @@ export function TransactionsTableClaimableRow({ () => sanitizeTokenSymbol(tx.asset, { erc20L1Address: tx.tokenAddress, - chain: getWagmiChain(tx.parentChainId) + chain: getWagmiChain( + isSourceChainIdEthereum ? tx.parentChainId : tx.childChainId + ) }), - [tx.asset, tx.tokenAddress, tx.parentChainId] + [ + tx.asset, + tx.tokenAddress, + tx.parentChainId, + tx.childChainId, + isSourceChainIdEthereum + ] ) const customAddressTxPadding = useMemo( From b350668acd0da866ed896eafdb4ab912696b7621 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 12 Dec 2023 17:39:53 +0100 Subject: [PATCH 42/72] nit --- .../TransactionHistory/TransactionsTableClaimableRow.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx index 325b03d1df..31001a95d4 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx @@ -305,8 +305,8 @@ export function TransactionsTableClaimableRow({ const isError = tx.status === 'Failure' const sourceChainId = tx.cctpData?.sourceChainId ?? ChainId.ArbitrumOne const { - isArbitrum: isSourceChainIdArbitrum, - isEthereumMainnetOrTestnet: isSourceChainIdEthereum + isEthereumMainnetOrTestnet: isSourceChainIdEthereum, + isArbitrum: isSourceChainIdArbitrum } = isNetwork(sourceChainId) const { address } = useAccount() From 5ca9b1f7f7630fb4031d6d8e2df7be1e6d2c3a8d Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 13 Dec 2023 17:00:46 +0100 Subject: [PATCH 43/72] fix --- .../TransactionsTableClaimableRow.tsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx index b7c1478ea7..31001a95d4 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx @@ -304,7 +304,10 @@ export function TransactionsTableClaimableRow({ }) { const isError = tx.status === 'Failure' const sourceChainId = tx.cctpData?.sourceChainId ?? ChainId.ArbitrumOne - const { isArbitrum: isSourceChainIdArbitrum } = isNetwork(sourceChainId) + const { + isEthereumMainnetOrTestnet: isSourceChainIdEthereum, + isArbitrum: isSourceChainIdArbitrum + } = isNetwork(sourceChainId) const { address } = useAccount() const bgClassName = useMemo(() => { @@ -317,9 +320,17 @@ export function TransactionsTableClaimableRow({ () => sanitizeTokenSymbol(tx.asset, { erc20L1Address: tx.tokenAddress, - chain: getWagmiChain(tx.parentChainId) + chain: getWagmiChain( + isSourceChainIdEthereum ? tx.parentChainId : tx.childChainId + ) }), - [tx.asset, tx.tokenAddress, tx.parentChainId] + [ + tx.asset, + tx.tokenAddress, + tx.parentChainId, + tx.childChainId, + isSourceChainIdEthereum + ] ) const customAddressTxPadding = useMemo( From d20fe7978dc6c3631f25cb450a12dbdd1f590175 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 14 Dec 2023 16:43:18 +0100 Subject: [PATCH 44/72] claiming --- .../TransactionsTableRowAction.tsx | 21 +++-------- .../src/hooks/arbTokenBridge.types.ts | 4 +-- .../src/hooks/useArbTokenBridge.ts | 13 +++---- .../src/hooks/useClaimWithdrawal.ts | 36 ++++++++++++++++--- 4 files changed, 43 insertions(+), 31 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRowAction.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRowAction.tsx index debaaa9af6..9111ffaf06 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRowAction.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRowAction.tsx @@ -3,7 +3,6 @@ import { EllipsisVerticalIcon } from '@heroicons/react/24/outline' import dayjs from 'dayjs' import { twMerge } from 'tailwind-merge' import { useMemo } from 'react' -import { useChainId } from 'wagmi' import { GET_HELP_LINK } from '../../constants' import { useClaimWithdrawal } from '../../hooks/useClaimWithdrawal' import { useNetworksAndSigners } from '../../hooks/useNetworksAndSigners' @@ -11,7 +10,7 @@ import { MergedTransaction } from '../../state/app/state' import { useClaimCctp, useRemainingTime } from '../../state/cctpState' import { shouldTrackAnalytics, trackEvent } from '../../util/AnalyticsUtils' import { isUserRejectedError } from '../../util/isUserRejectedError' -import { getNetworkName, isNetwork } from '../../util/networks' +import { getNetworkName } from '../../util/networks' import { errorToast } from '../common/atoms/Toast' import { Button } from '../common/Button' import { Tooltip } from '../common/Tooltip' @@ -53,26 +52,16 @@ export function TransactionsTableRowAction({ const l2NetworkName = getNetworkName(l2Network.id) const networkName = type === 'deposits' ? l1NetworkName : l2NetworkName - const chainId = useChainId() const { claim, isClaiming } = useClaimWithdrawal() const { claim: claimCctp, isClaiming: isClaimingCctp } = useClaimCctp(tx) const { isConfirmed } = useRemainingTime(tx) - const { isEthereumMainnetOrTestnet, isArbitrum } = isNetwork(chainId) - const currentChainIsValid = useMemo(() => { - const isWithdrawalSourceOrbitChain = isNetwork(tx.childChainId).isOrbitChain - - if (isWithdrawalSourceOrbitChain) { - // Enable claim if withdrawn from an Orbit chain and is connected to L2 - return isArbitrum + if (type === 'deposits') { + return l2Network.id === tx.childChainId } - - return ( - (type === 'deposits' && isArbitrum) || - (type === 'withdrawals' && isEthereumMainnetOrTestnet) - ) - }, [isArbitrum, isEthereumMainnetOrTestnet, type, tx.childChainId]) + return l1Network.id === tx.parentChainId + }, [type, l1Network.id, tx.parentChainId, tx.childChainId, l2Network.id]) const isClaimButtonDisabled = useMemo(() => { return isClaiming || isClaimingCctp || !isConfirmed diff --git a/packages/arb-token-bridge-ui/src/hooks/arbTokenBridge.types.ts b/packages/arb-token-bridge-ui/src/hooks/arbTokenBridge.types.ts index e8bac5df22..28201f1a4b 100644 --- a/packages/arb-token-bridge-ui/src/hooks/arbTokenBridge.types.ts +++ b/packages/arb-token-bridge-ui/src/hooks/arbTokenBridge.types.ts @@ -164,7 +164,7 @@ export interface ArbTokenBridgeEth { txLifecycle?: L2ContractCallTransactionLifecycle }) => Promise triggerOutbox: (params: { - id: string + event: L2ToL1EventResultPlus | undefined l1Signer: Signer }) => Promise } @@ -198,7 +198,7 @@ export interface ArbTokenBridgeToken { destinationAddress?: string }) => Promise triggerOutbox: (params: { - id: string + event: L2ToL1EventResultPlus | undefined l1Signer: Signer }) => Promise } diff --git a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts index 135c950637..e0630606a1 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts @@ -803,14 +803,12 @@ export const useArbTokenBridge = ( } async function triggerOutboxToken({ - id, + event, l1Signer }: { - id: string + event: L2ToL1EventResultPlus | undefined l1Signer: Signer }) { - const event = pendingWithdrawalsMap[id] - if (!event) { throw new Error('Outbox message not found') } @@ -822,7 +820,6 @@ export const useArbTokenBridge = ( const { tokenAddress, value } = event const messageWriter = L2ToL1Message.fromEvent(l1Signer, event, l1.provider) - const res = await messageWriter.execute(l2.provider) const { symbol, decimals } = await fetchErc20Data({ @@ -885,14 +882,12 @@ export const useArbTokenBridge = ( } async function triggerOutboxEth({ - id, + event, l1Signer }: { - id: string + event: L2ToL1EventResultPlus | undefined l1Signer: Signer }) { - const event = pendingWithdrawalsMap[id] - if (!event) { throw new Error('Outbox message not found') } diff --git a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts index 4200343733..1592fb6675 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts @@ -6,7 +6,10 @@ import { useAppState } from '../state' import { MergedTransaction } from '../state/app/state' import { isUserRejectedError } from '../util/isUserRejectedError' import { errorToast } from '../components/common/atoms/Toast' -import { AssetType } from './arbTokenBridge.types' +import { AssetType, L2ToL1EventResultPlus } from './arbTokenBridge.types' +import { getProvider } from '../components/TransactionHistory/helpers' +import { L2TransactionReceipt } from '@arbitrum/sdk' +import { utils } from 'ethers' export type UseClaimWithdrawalResult = { claim: (tx: MergedTransaction) => Promise @@ -21,7 +24,7 @@ export function useClaimWithdrawal(): UseClaimWithdrawalResult { const [isClaiming, setIsClaiming] = useState(false) async function claim(tx: MergedTransaction) { - if (isClaiming) { + if (isClaiming || !tx.isWithdrawal || tx.isCctp) { return } @@ -33,18 +36,43 @@ export function useClaimWithdrawal(): UseClaimWithdrawalResult { setIsClaiming(true) + const childChainProvider = getProvider(tx.childChainId) + const txReceipt = await childChainProvider.getTransactionReceipt(tx.txId) + const l2TxReceipt = new L2TransactionReceipt(txReceipt) + const [event] = l2TxReceipt.getL2ToL1Events() + + if (!event) { + return + } + + const extendedEvent: L2ToL1EventResultPlus = { + ...event, + sender: tx.sender, + destinationAddress: tx.destination, + l2TxHash: tx.txId, + type: tx.assetType, + value: utils.parseEther(tx.value ?? '0'), + tokenAddress: tx.tokenAddress || undefined, + outgoingMessageState: 1, + symbol: tx.asset, + decimals: 18, + nodeBlockDeadline: tx.nodeBlockDeadline, + parentChainId: tx.parentChainId, + childChainId: tx.childChainId + } + try { if (!signer) { throw 'Signer is undefined' } if (tx.assetType === AssetType.ETH) { res = await arbTokenBridge.eth.triggerOutbox({ - id: tx.uniqueId.toString(), + event: extendedEvent, l1Signer: signer }) } else { res = await arbTokenBridge.token.triggerOutbox({ - id: tx.uniqueId.toString(), + event: extendedEvent, l1Signer: signer }) } From e2cec5458ae9f168a9c1f0dc68594700959c8b61 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 14 Dec 2023 16:46:26 +0100 Subject: [PATCH 45/72] claiming clean up --- packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts index 1592fb6675..c526299e79 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts @@ -42,7 +42,8 @@ export function useClaimWithdrawal(): UseClaimWithdrawalResult { const [event] = l2TxReceipt.getL2ToL1Events() if (!event) { - return + setIsClaiming(false) + throw new Error('Event not found.') } const extendedEvent: L2ToL1EventResultPlus = { From f6bf77507a893b5fd2c9c90ef2086d682038d6da Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 14 Dec 2023 16:47:49 +0100 Subject: [PATCH 46/72] claiming clean up --- packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts index c526299e79..dbe45084c0 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts @@ -43,7 +43,8 @@ export function useClaimWithdrawal(): UseClaimWithdrawalResult { if (!event) { setIsClaiming(false) - throw new Error('Event not found.') + errorToast("Can't claim withdrawal: event not found.") + return } const extendedEvent: L2ToL1EventResultPlus = { From fb93b23457c63380a67a2addb48e4c83534d0d66 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 14 Dec 2023 17:01:21 +0100 Subject: [PATCH 47/72] updates --- .../src/hooks/useArbTokenBridge.ts | 31 ------------------- .../src/hooks/useClaimWithdrawal.ts | 28 ++++++++++++++--- 2 files changed, 23 insertions(+), 36 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts index e0630606a1..9f16c33afe 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts @@ -817,44 +817,13 @@ export const useArbTokenBridge = ( return } - const { tokenAddress, value } = event - const messageWriter = L2ToL1Message.fromEvent(l1Signer, event, l1.provider) const res = await messageWriter.execute(l2.provider) - const { symbol, decimals } = await fetchErc20Data({ - address: tokenAddress as string, - provider: l1.provider - }) - - addTransaction({ - status: 'pending', - type: 'outbox', - value: utils.formatUnits(value, decimals), - assetName: symbol, - assetType: AssetType.ERC20, - sender: walletAddress, - txID: res.hash, - l1NetworkID, - l2ToL1MsgData: { uniqueId: getUniqueIdOrHashFromEvent(event) } - }) - const rec = await res.wait() if (rec.status === 1) { - setTransactionSuccess(rec.transactionHash) addToExecutedMessagesCache([event]) - setPendingWithdrawalMap(oldPendingWithdrawalsMap => { - const newPendingWithdrawalsMap = { ...oldPendingWithdrawalsMap } - const pendingWithdrawal = newPendingWithdrawalsMap[id] - if (pendingWithdrawal) { - pendingWithdrawal.outgoingMessageState = OutgoingMessageState.EXECUTED - } - - return newPendingWithdrawalsMap - }) - } else { - setTransactionFailure(rec.transactionHash) } return rec diff --git a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts index dbe45084c0..4edca60d7c 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts @@ -1,15 +1,18 @@ import { useState } from 'react' import * as Sentry from '@sentry/react' -import { useSigner } from 'wagmi' +import { useAccount, useSigner } from 'wagmi' import { useAppState } from '../state' -import { MergedTransaction } from '../state/app/state' +import { MergedTransaction, WithdrawalStatus } from '../state/app/state' import { isUserRejectedError } from '../util/isUserRejectedError' import { errorToast } from '../components/common/atoms/Toast' import { AssetType, L2ToL1EventResultPlus } from './arbTokenBridge.types' import { getProvider } from '../components/TransactionHistory/helpers' import { L2TransactionReceipt } from '@arbitrum/sdk' -import { utils } from 'ethers' +import { ContractReceipt, utils } from 'ethers' +import { useTransactionHistory } from './useTransactionHistory' +import dayjs from 'dayjs' +import { fetchErc20Data } from '../util/TokenUtils' export type UseClaimWithdrawalResult = { claim: (tx: MergedTransaction) => Promise @@ -20,7 +23,9 @@ export function useClaimWithdrawal(): UseClaimWithdrawalResult { const { app: { arbTokenBridge } } = useAppState() + const { address } = useAccount() const { data: signer } = useSigner() + const { updatePendingTransaction } = useTransactionHistory(address) const [isClaiming, setIsClaiming] = useState(false) async function claim(tx: MergedTransaction) { @@ -47,6 +52,11 @@ export function useClaimWithdrawal(): UseClaimWithdrawalResult { return } + const { symbol, decimals } = await fetchErc20Data({ + address: tx.tokenAddress as string, + provider: getProvider(tx.parentChainId) + }) + const extendedEvent: L2ToL1EventResultPlus = { ...event, sender: tx.sender, @@ -56,8 +66,8 @@ export function useClaimWithdrawal(): UseClaimWithdrawalResult { value: utils.parseEther(tx.value ?? '0'), tokenAddress: tx.tokenAddress || undefined, outgoingMessageState: 1, - symbol: tx.asset, - decimals: 18, + symbol: symbol, + decimals: decimals ?? 18, nodeBlockDeadline: tx.nodeBlockDeadline, parentChainId: tx.parentChainId, childChainId: tx.childChainId @@ -93,6 +103,14 @@ export function useClaimWithdrawal(): UseClaimWithdrawalResult { if (!res) { errorToast(`Can't claim withdrawal: ${err?.message ?? err}`) } + + const isSuccess = (res as ContractReceipt).status === 1 + + updatePendingTransaction({ + ...tx, + status: isSuccess ? WithdrawalStatus.EXECUTED : WithdrawalStatus.FAILURE, + resolvedAt: isSuccess ? dayjs().valueOf() : null + }) } return { claim, isClaiming } From 237455dba68655fd55de123e36da1d78ed3b7ffd Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 14 Dec 2023 17:05:25 +0100 Subject: [PATCH 48/72] fixes --- .../src/hooks/useArbTokenBridge.ts | 26 ------------------- .../src/hooks/useClaimWithdrawal.ts | 15 ++++++----- 2 files changed, 9 insertions(+), 32 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts index 9f16c33afe..d024dae8df 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts @@ -865,40 +865,14 @@ export const useArbTokenBridge = ( return } - const { value } = event - const messageWriter = L2ToL1Message.fromEvent(l1Signer, event, l1.provider) const res = await messageWriter.execute(l2.provider) - addTransaction({ - status: 'pending', - type: 'outbox', - value: utils.formatUnits(value, nativeCurrency.decimals), - assetName: nativeCurrency.symbol, - assetType: AssetType.ETH, - sender: walletAddress, - txID: res.hash, - l1NetworkID, - l2ToL1MsgData: { uniqueId: getUniqueIdOrHashFromEvent(event) } - }) - const rec = await res.wait() if (rec.status === 1) { - setTransactionSuccess(rec.transactionHash) addToExecutedMessagesCache([event]) - setPendingWithdrawalMap(oldPendingWithdrawalsMap => { - const newPendingWithdrawalsMap = { ...oldPendingWithdrawalsMap } - const pendingWithdrawal = newPendingWithdrawalsMap[id] - if (pendingWithdrawal) { - pendingWithdrawal.outgoingMessageState = OutgoingMessageState.EXECUTED - } - - return newPendingWithdrawalsMap - }) - } else { - setTransactionFailure(rec.transactionHash) } return rec diff --git a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts index 4edca60d7c..92c62c2b79 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts @@ -52,10 +52,13 @@ export function useClaimWithdrawal(): UseClaimWithdrawalResult { return } - const { symbol, decimals } = await fetchErc20Data({ - address: tx.tokenAddress as string, - provider: getProvider(tx.parentChainId) - }) + const { symbol, decimals } = + tx.assetType === AssetType.ERC20 + ? await fetchErc20Data({ + address: tx.tokenAddress as string, + provider: getProvider(tx.parentChainId) + }) + : { symbol: tx.asset, decimals: 18 } const extendedEvent: L2ToL1EventResultPlus = { ...event, @@ -66,8 +69,8 @@ export function useClaimWithdrawal(): UseClaimWithdrawalResult { value: utils.parseEther(tx.value ?? '0'), tokenAddress: tx.tokenAddress || undefined, outgoingMessageState: 1, - symbol: symbol, - decimals: decimals ?? 18, + symbol, + decimals, nodeBlockDeadline: tx.nodeBlockDeadline, parentChainId: tx.parentChainId, childChainId: tx.childChainId From 1307f5f2930f94df00dc958b27891bd00efe5bb6 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 14 Dec 2023 18:46:09 +0100 Subject: [PATCH 49/72] testnet mode --- .../src/hooks/useTransactionHistory.ts | 96 +++++++++++++------ 1 file changed, 67 insertions(+), 29 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 33c60792ba..375053fdc4 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -2,8 +2,9 @@ import useSWRImmutable from 'swr/immutable' import useSWRInfinite from 'swr/infinite' import { useCallback, useEffect, useMemo, useState } from 'react' import dayjs from 'dayjs' +import { useLocalStorage } from 'react-use' -import { ChainId } from '../util/networks' +import { ChainId, isNetwork } from '../util/networks' import { fetchWithdrawals } from '../util/withdrawals/fetchWithdrawals' import { fetchDeposits } from '../util/deposits/fetchDeposits' import { @@ -37,6 +38,7 @@ import { isSameTransaction, isTxPending } from '../components/TransactionHistory/helpers' +import { testnetModeLocalStorageKey } from '../components/common/SettingsDialog' export type Deposit = Transaction @@ -48,6 +50,8 @@ export type Withdrawal = type DepositOrWithdrawal = Deposit | Withdrawal type Transfer = DepositOrWithdrawal | MergedTransaction +type ChainPair = { parentChain: ChainId; chain: ChainId } + export type TransactionHistoryParams = { data: { transactions: MergedTransaction[] @@ -84,7 +88,7 @@ function sortByTimestampDescending(a: Transfer, b: Transfer) { : 1 } -const multiChainFetchList: { parentChain: ChainId; chain: ChainId }[] = [ +const multiChainFetchList: ChainPair[] = [ { parentChain: ChainId.Ethereum, chain: ChainId.ArbitrumOne @@ -179,12 +183,20 @@ async function transformTransaction(tx: Transfer): Promise { ) } +function isTestnetChainPair(chainPair: ChainPair) { + return isNetwork(chainPair.parentChain).isTestnet +} + /** * Fetches transaction history only for deposits and withdrawals, without their statuses. */ const useTransactionHistoryWithoutStatuses = ( address: `0x${string}` | undefined ) => { + const [isTestnetMode = false] = useLocalStorage( + testnetModeLocalStorageKey + ) + const cctpTransfersMainnet = useCctpFetching({ walletAddress: address, l1ChainId: ChainId.Ethereum, @@ -199,7 +211,7 @@ const useTransactionHistoryWithoutStatuses = ( l1ChainId: ChainId.Goerli, l2ChainId: ChainId.ArbitrumGoerli, pageNumber: 0, - pageSize: 1000, + pageSize: isTestnetMode ? 1000 : 0, type: 'all' }) @@ -226,19 +238,28 @@ const useTransactionHistoryWithoutStatuses = ( error: depositsError, isLoading: depositsLoading } = useSWRImmutable( - address ? ['tx_list', 'deposits', address] : null, - ([, , _address]) => { + address ? ['tx_list', 'deposits', address, isTestnetMode] : null, + ([, , _address, _isTestnetMode]) => { return Promise.all( - multiChainFetchList.map(chainPair => { - return fetchDeposits({ - sender: _address, - receiver: _address, - l1Provider: getProvider(chainPair.parentChain), - l2Provider: getProvider(chainPair.chain), - pageNumber: 0, - pageSize: 1000 + multiChainFetchList + .filter(chainPair => { + if (_isTestnetMode) { + // in testnet mode we fetch all chain pairs + return true + } + // otherwise don't fetch testnet chain pairs + return !isTestnetChainPair(chainPair) + }) + .map(chainPair => { + return fetchDeposits({ + sender: _address, + receiver: _address, + l1Provider: getProvider(chainPair.parentChain), + l2Provider: getProvider(chainPair.chain), + pageNumber: 0, + pageSize: 1000 + }) }) - }) ) } ) @@ -248,19 +269,28 @@ const useTransactionHistoryWithoutStatuses = ( error: withdrawalsError, isLoading: withdrawalsLoading } = useSWRImmutable( - address ? ['tx_list', 'withdrawals', address] : null, - ([, , _address]) => { + address ? ['tx_list', 'withdrawals', address, isTestnetMode] : null, + ([, , _address, _isTestnetMode]) => { return Promise.all( - multiChainFetchList.map(chainPair => { - return fetchWithdrawals({ - sender: _address, - receiver: _address, - l1Provider: getProvider(chainPair.parentChain), - l2Provider: getProvider(chainPair.chain), - pageNumber: 0, - pageSize: 1000 + multiChainFetchList + .filter(chainPair => { + if (_isTestnetMode) { + // in testnet mode we fetch all chain pairs + return chainPair + } + // otherwise don't fetch testnet chain pairs + return !isTestnetChainPair(chainPair) + }) + .map(chainPair => { + return fetchWithdrawals({ + sender: _address, + receiver: _address, + l1Provider: getProvider(chainPair.parentChain), + l2Provider: getProvider(chainPair.chain), + pageNumber: 0, + pageSize: 1000 + }) }) - }) ) } ) @@ -306,10 +336,10 @@ export const useTransactionHistory = ( } return address && !loading - ? (['complete_tx_list', address, pageNumber] as const) + ? (['complete_tx_list', address, pageNumber, data] as const) : null }, - [address, loading] + [address, loading, data] ) const { @@ -321,12 +351,12 @@ export const useTransactionHistory = ( isValidating } = useSWRInfinite( getCacheKey, - ([, , _page]) => { + ([, , _page, _data]) => { const startIndex = _page * MAX_BATCH_SIZE const endIndex = startIndex + MAX_BATCH_SIZE return Promise.all( - data.slice(startIndex, endIndex).map(transformTransaction) + _data.slice(startIndex, endIndex).map(transformTransaction) ) }, { @@ -342,6 +372,14 @@ export const useTransactionHistory = ( } ) + useEffect(() => { + if (runFetcher && !loading) { + // when data changes (e.g. on address change) + setPage(1) + setFetching(true) + } + }, [JSON.stringify(data), runFetcher, loading]) + // transfers initiated by the user during the current session // we store it separately as there are a lot of side effects when mutating SWRInfinite const { data: newTransactionsData, mutate: mutateNewTransactionsData } = From 40e554ecb830ffcf8852f62737457ae0bf77ec6c Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 14 Dec 2023 18:56:59 +0100 Subject: [PATCH 50/72] custom orbit chains --- .../src/hooks/useTransactionHistory.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 375053fdc4..9f02456086 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -4,7 +4,11 @@ import { useCallback, useEffect, useMemo, useState } from 'react' import dayjs from 'dayjs' import { useLocalStorage } from 'react-use' -import { ChainId, isNetwork } from '../util/networks' +import { + ChainId, + getCustomChainsFromLocalStorage, + isNetwork +} from '../util/networks' import { fetchWithdrawals } from '../util/withdrawals/fetchWithdrawals' import { fetchDeposits } from '../util/deposits/fetchDeposits' import { @@ -114,7 +118,13 @@ const multiChainFetchList: ChainPair[] = [ { parentChain: ChainId.ArbitrumSepolia, chain: ChainId.StylusTestnet - } + }, + ...getCustomChainsFromLocalStorage().map(chain => { + return { + parentChain: chain.partnerChainID, + chain: chain.chainID + } + }) ] function isWithdrawalFromSubgraph( From 3e57e91bb44118a490b72701c8032406a18a3cde Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 14 Dec 2023 19:25:34 +0100 Subject: [PATCH 51/72] token timestamps --- .../src/util/withdrawals/fetchWithdrawals.ts | 14 ++++++++++++-- .../src/util/withdrawals/helpers.ts | 5 ----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/util/withdrawals/fetchWithdrawals.ts b/packages/arb-token-bridge-ui/src/util/withdrawals/fetchWithdrawals.ts index c15df4807e..c97ff70ea3 100644 --- a/packages/arb-token-bridge-ui/src/util/withdrawals/fetchWithdrawals.ts +++ b/packages/arb-token-bridge-ui/src/util/withdrawals/fetchWithdrawals.ts @@ -7,6 +7,8 @@ import { tryFetchLatestSubgraphBlockNumber } from '../SubgraphUtils' import { fetchTokenWithdrawalsFromEventLogs } from './fetchTokenWithdrawalsFromEventLogs' import { fetchL2Gateways } from '../fetchL2Gateways' import { Withdrawal } from '../../hooks/useTransactionHistory' +import { attachTimestampToTokenWithdrawal } from './helpers' +import { WithdrawalInitiated } from '../../hooks/arbTokenBridge.types' export type FetchWithdrawalsParams = { sender?: string @@ -109,7 +111,7 @@ export async function fetchWithdrawals({ } }) - const mappedTokenWithdrawalsFromEventLogs: Withdrawal[] = + const mappedTokenWithdrawalsFromEventLogs: WithdrawalInitiated[] = tokenWithdrawalsFromEventLogs.map(tx => { return { ...tx, @@ -120,8 +122,16 @@ export async function fetchWithdrawals({ } }) + // we need timestamps to sort token withdrawals along ETH withdrawals + const tokenWithdrawalsFromEventLogsWithTimestamp: Withdrawal[] = + await Promise.all( + mappedTokenWithdrawalsFromEventLogs.map(withdrawal => + attachTimestampToTokenWithdrawal({ withdrawal, l2Provider }) + ) + ) + return [ ...mappedEthWithdrawalsFromEventLogs, - ...mappedTokenWithdrawalsFromEventLogs + ...tokenWithdrawalsFromEventLogsWithTimestamp ] } diff --git a/packages/arb-token-bridge-ui/src/util/withdrawals/helpers.ts b/packages/arb-token-bridge-ui/src/util/withdrawals/helpers.ts index dfc95bc4dd..eb35961bc6 100644 --- a/packages/arb-token-bridge-ui/src/util/withdrawals/helpers.ts +++ b/packages/arb-token-bridge-ui/src/util/withdrawals/helpers.ts @@ -50,11 +50,6 @@ export async function attachTimestampToTokenWithdrawal({ withdrawal: WithdrawalInitiated l2Provider: Provider }) { - return { - ...withdrawal, - // TODO: arbitrary number, need changes to the SDK to get token withdrawal timestamp - timestamp: BigNumber.from(1665914495) - } const txReceipt = await l2Provider.getTransactionReceipt(withdrawal.txHash) const l2TxReceipt = new L2TransactionReceipt(txReceipt) const [event] = l2TxReceipt.getL2ToL1Events() From 9d1d5de21034ffdd232e465dbcb911c5ce0121d1 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 14 Dec 2023 20:57:32 +0100 Subject: [PATCH 52/72] custom orbit chains --- .../src/hooks/useTransactionHistory.ts | 83 +++++++++---------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 9f02456086..a89009fa19 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -6,6 +6,7 @@ import { useLocalStorage } from 'react-use' import { ChainId, + getCustomChainFromLocalStorageById, getCustomChainsFromLocalStorage, isNetwork } from '../util/networks' @@ -243,35 +244,55 @@ const useTransactionHistoryWithoutStatuses = ( cctpTransfersTestnet.isLoadingDeposits || cctpTransfersTestnet.isLoadingWithdrawals - const { - data: depositsData, - error: depositsError, - isLoading: depositsLoading - } = useSWRImmutable( - address ? ['tx_list', 'deposits', address, isTestnetMode] : null, - ([, , _address, _isTestnetMode]) => { + const fetcher = useCallback( + (type: 'deposits' | 'withdrawals') => { + const fetcherFn = type === 'deposits' ? fetchDeposits : fetchWithdrawals + return Promise.all( multiChainFetchList .filter(chainPair => { - if (_isTestnetMode) { + if (isTestnetMode) { // in testnet mode we fetch all chain pairs - return true + return chainPair } // otherwise don't fetch testnet chain pairs return !isTestnetChainPair(chainPair) }) - .map(chainPair => { - return fetchDeposits({ - sender: _address, - receiver: _address, - l1Provider: getProvider(chainPair.parentChain), - l2Provider: getProvider(chainPair.chain), - pageNumber: 0, - pageSize: 1000 - }) + .map(async chainPair => { + try { + return await fetcherFn({ + sender: address, + receiver: address, + l1Provider: getProvider(chainPair.parentChain), + l2Provider: getProvider(chainPair.chain), + pageNumber: 0, + pageSize: 1000 + }) + } catch (err) { + const isCustomOrbitChain = !!getCustomChainFromLocalStorageById( + chainPair.chain + ) + + if (isCustomOrbitChain) { + // don't throw for custom orbit chains, local node may be offline + return [] + } + + throw err + } }) ) - } + }, + [address, isTestnetMode] + ) + + const { + data: depositsData, + error: depositsError, + isLoading: depositsLoading + } = useSWRImmutable( + address ? ['tx_list', 'deposits', address, isTestnetMode] : null, + () => fetcher('deposits') ) const { @@ -280,29 +301,7 @@ const useTransactionHistoryWithoutStatuses = ( isLoading: withdrawalsLoading } = useSWRImmutable( address ? ['tx_list', 'withdrawals', address, isTestnetMode] : null, - ([, , _address, _isTestnetMode]) => { - return Promise.all( - multiChainFetchList - .filter(chainPair => { - if (_isTestnetMode) { - // in testnet mode we fetch all chain pairs - return chainPair - } - // otherwise don't fetch testnet chain pairs - return !isTestnetChainPair(chainPair) - }) - .map(chainPair => { - return fetchWithdrawals({ - sender: _address, - receiver: _address, - l1Provider: getProvider(chainPair.parentChain), - l2Provider: getProvider(chainPair.chain), - pageNumber: 0, - pageSize: 1000 - }) - }) - ) - } + () => fetcher('withdrawals') ) const deposits = (depositsData || []).flat() From 24fb120228c6040d2309acfc66ed4b8dd9209683 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 15 Dec 2023 14:23:59 +0100 Subject: [PATCH 53/72] ui fixes --- .../TransactionHistory/TransactionHistory.tsx | 3 +- .../TransactionHistoryTable.tsx | 51 +++-- .../TransactionsTableClaimableRow.tsx | 10 +- .../TransactionsTableDepositRow.tsx | 205 +++++++++--------- .../src/components/common/SidePanel.tsx | 2 +- 5 files changed, 147 insertions(+), 124 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx index 4e95eadada..a6ebdd8cfb 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx @@ -95,7 +95,6 @@ export const TransactionHistory = ({ @@ -115,6 +115,7 @@ export const TransactionHistory = ({ resume={resume} rowHeight={85} rowHeightCustomDestinationAddress={117} + selectedTabIndex={1} /> diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx index 9be4b78b39..b42b69c89c 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx @@ -7,7 +7,8 @@ import { MergedTransaction } from '../../state/app/state' import { getStandardizedDate, getStandardizedTime, - isCustomDestinationAddressTx + isCustomDestinationAddressTx, + isPending } from '../../state/app/utils' import { TransactionsTableClaimableRow } from './TransactionsTableClaimableRow' import { TransactionsTableDepositRow } from './TransactionsTableDepositRow' @@ -79,17 +80,16 @@ const TableHeader = ({ export const TransactionHistoryTable = ({ transactions, - className, loading, completed, error, numberOfDays, resume, rowHeight, - rowHeightCustomDestinationAddress + rowHeightCustomDestinationAddress, + selectedTabIndex }: { transactions: MergedTransaction[] - className?: string loading: boolean completed: boolean error: unknown @@ -97,8 +97,10 @@ export const TransactionHistoryTable = ({ resume: () => void rowHeight: number rowHeightCustomDestinationAddress: number + selectedTabIndex: number }) => { const isTxHistoryEmpty = transactions.length === 0 + const isPendingTab = selectedTabIndex === 0 const paused = !loading && !completed @@ -174,8 +176,13 @@ export const TransactionHistoryTable = ({ } return ( -
-
+
+
0 ? 'rounded-tl-lg' : '' + )} + > {loading ? (
@@ -184,7 +191,9 @@ export const TransactionHistoryTable = ({ ) : (
- Showing transactions for the last {numberOfDaysString}. + Showing {transactions.length}{' '} + {isPendingTab ? 'pending' : 'settled'} transactions for the last{' '} + {numberOfDaysString}. {!completed && ( @@ -199,17 +208,15 @@ export const TransactionHistoryTable = ({ )}
- {({ width, height }) => ( + {({ height }) => ( getRowHeight(index)} rowCount={transactions.length} headerHeight={52} headerRowRenderer={props => ( -
- {props.columns} -
+
{props.columns}
)} className="table-auto" rowGetter={({ index }) => transactions[index]} @@ -221,6 +228,9 @@ export const TransactionHistoryTable = ({ return null } + const bgColorSettled = isEvenRow ? 'bg-cyan' : 'bg-white' + const bgColorPending = 'bg-orange' + return (
) : ( )}
) }} > - {/* TODO: FIX LAYOUT FOR HEADERS AND COLUMNS: WIDTH AND PADDING */} ( Status )} @@ -253,7 +266,7 @@ export const TransactionHistoryTable = ({ ( Date )} @@ -261,7 +274,7 @@ export const TransactionHistoryTable = ({ ( Token )} @@ -269,7 +282,7 @@ export const TransactionHistoryTable = ({ ( Networks )} diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx index 31001a95d4..7bec940071 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx @@ -353,7 +353,7 @@ export function TransactionsTableClaimableRow({ - + + - + - + - + - - {isCustomDestinationAddressTx(tx) && ( - - )} - + {isCustomDestinationAddressTx(tx) && ( + + )} + + ) } diff --git a/packages/arb-token-bridge-ui/src/components/common/SidePanel.tsx b/packages/arb-token-bridge-ui/src/components/common/SidePanel.tsx index d0e61fe835..9a2b00ca34 100644 --- a/packages/arb-token-bridge-ui/src/components/common/SidePanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/common/SidePanel.tsx @@ -80,7 +80,7 @@ export const SidePanel = ({ > From 589da168e3c8f85362d416f84ba0fbedde7cb9b1 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 15 Dec 2023 14:36:39 +0100 Subject: [PATCH 54/72] fix --- .../TransactionHistory/TransactionsTableClaimableRow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx index 7bec940071..ab14ecc45d 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx @@ -393,7 +393,7 @@ export function TransactionsTableClaimableRow({
@@ -365,7 +365,7 @@ export function TransactionsTableClaimableRow({ @@ -377,7 +377,7 @@ export function TransactionsTableClaimableRow({ @@ -393,7 +393,7 @@ export function TransactionsTableClaimableRow({ @@ -405,7 +405,7 @@ export function TransactionsTableClaimableRow({ diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDepositRow.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDepositRow.tsx index 3c393a774b..e8a1426301 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDepositRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDepositRow.tsx @@ -15,6 +15,7 @@ import { InformationCircleIcon } from '@heroicons/react/24/outline' import { isCustomDestinationAddressTx, isDepositReadyToRedeem, + isFailed, isPending } from '../../state/app/utils' import { TokenIcon, TransactionDateTime } from './TransactionHistoryTable' @@ -281,121 +282,129 @@ export function TransactionsTableDepositRow({ [tx] ) + const rowHeight = useMemo(() => { + if (isPending(tx) || isFailed(tx)) { + return isCustomDestinationAddressTx(tx) ? 'h-[126px]' : 'h-[94px]' + } + return isCustomDestinationAddressTx(tx) ? 'h-[117px]' : 'h-[85px]' + }, [tx]) + if (!tx.sender || !address) { return null } return ( -
- -
+ + - - + + -
- - - {formatAmount(Number(tx.value), { - symbol: tokenSymbol - })} - -
-
+
+ + + {formatAmount(Number(tx.value), { + symbol: tokenSymbol + })} + +
+
- - + + - {showRedeemRetryableButton && ( - - Please connect to the L2 network to re-execute your deposit. You - have 7 days to re-execute a failed tx. After that, the tx is no - longer recoverable. - - } - > - + {showRedeemRetryableButton && ( + + Please connect to the L2 network to re-execute your deposit. + You have 7 days to re-execute a failed tx. After that, the tx + is no longer recoverable. + + } > - Retry - - - )} - - {showRetryableExpiredText && ( - - You have 7 days to re-execute a failed tx. After that, the tx is - no longer recoverable. + + + )} + + {showRetryableExpiredText && ( + + You have 7 days to re-execute a failed tx. After that, the tx + is no longer recoverable. + + } + > + + EXPIRED - } - > - - EXPIRED - - - )} - - {tx.isCctp && ( - - )} - - + + )} + + {tx.isCctp && ( + + )}
+ +
From b254b73610208cb7023729b4034aa01b0bfa5e41 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 15 Dec 2023 15:08:35 +0100 Subject: [PATCH 55/72] fix --- .../TransactionHistoryTable.tsx | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx index b42b69c89c..0049613dd2 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx @@ -124,6 +124,19 @@ export const TransactionHistoryTable = ({ }` if (isTxHistoryEmpty) { + if (loading) { + return ( +
0 ? 'rounded-tl-lg' : '' + )} + > + + Loading transactions... +
+ ) + } if (error) { return (
@@ -142,14 +155,6 @@ export const TransactionHistoryTable = ({
) } - if (loading) { - return ( -
- - Loading transactions... -
- ) - } if (paused) { return (
From f910b8c278af6957a1f1fcb3b62db0743c9ce916 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 18 Dec 2023 15:35:27 +0100 Subject: [PATCH 56/72] caching txs --- .../src/components/App/App.tsx | 3 - .../TransactionsTableClaimableRow.tsx | 41 +++--- .../components/TransactionHistory/helpers.ts | 110 +++++++++++++++- .../syncers/RetryableTxnsIncluder.tsx | 118 ------------------ .../src/hooks/useArbTokenBridge.ts | 50 ++++++++ .../src/hooks/useClaimWithdrawal.ts | 10 +- .../src/hooks/useTransactionHistory.ts | 27 +++- .../src/state/app/utils.ts | 27 ---- 8 files changed, 207 insertions(+), 179 deletions(-) delete mode 100644 packages/arb-token-bridge-ui/src/components/syncers/RetryableTxnsIncluder.tsx diff --git a/packages/arb-token-bridge-ui/src/components/App/App.tsx b/packages/arb-token-bridge-ui/src/components/App/App.tsx index ba0511243c..bfb56b5758 100644 --- a/packages/arb-token-bridge-ui/src/components/App/App.tsx +++ b/packages/arb-token-bridge-ui/src/components/App/App.tsx @@ -18,7 +18,6 @@ import { Alert } from '../common/Alert' import { MainContent } from '../MainContent/MainContent' import { ArbTokenBridgeStoreSync } from '../syncers/ArbTokenBridgeStoreSync' import { BalanceUpdater } from '../syncers/BalanceUpdater' -import { RetryableTxnsIncluder } from '../syncers/RetryableTxnsIncluder' import { TokenListSyncer } from '../syncers/TokenListSyncer' import { useDialog } from '../common/Dialog' import { @@ -119,8 +118,6 @@ const AppContent = (): JSX.Element => { - - diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx index ab14ecc45d..a231823a13 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableClaimableRow.tsx @@ -16,11 +16,7 @@ import { isNetwork } from '../../util/networks' import { InformationCircleIcon } from '@heroicons/react/24/outline' -import { - isCustomDestinationAddressTx, - findMatchingL1TxForWithdrawal, - isPending -} from '../../state/app/utils' +import { isCustomDestinationAddressTx, isPending } from '../../state/app/utils' import { TokenIcon, TransactionDateTime } from './TransactionHistoryTable' import { formatAmount } from '../../util/NumberUtils' import { sanitizeTokenSymbol } from '../../util/TokenUtils' @@ -29,6 +25,7 @@ import { useRemainingTime } from '../../state/cctpState' import { useChainLayers } from '../../hooks/useChainLayers' import { getWagmiChain } from '../../util/wagmi/getWagmiChain' import { NetworkImage } from '../common/NetworkImage' +import { getWithdrawalClaimParentChainTxDetails } from './helpers' type CommonProps = { tx: MergedTransaction @@ -37,9 +34,9 @@ type CommonProps = { function ClaimableRowStatus({ tx }: CommonProps) { const { parentLayer, layer } = useChainLayers() - const matchingL1Tx = tx.isCctp + const matchingL1TxId = tx.isCctp ? tx.cctpData?.receiveMessageTransactionHash - : findMatchingL1TxForWithdrawal(tx) + : getWithdrawalClaimParentChainTxDetails(tx)?.txId switch (tx.status) { case 'pending': @@ -96,7 +93,7 @@ function ClaimableRowStatus({ tx }: CommonProps) { ) case 'Executed': { - if (typeof matchingL1Tx === 'undefined') { + if (typeof matchingL1TxId === 'undefined') { return (
{layer} Transaction time}> @@ -213,10 +208,10 @@ function ClaimableRowTime({ tx }: CommonProps) { {layer} Transaction Time}> - {claimedTx?.createdAt && ( + {claimedTxTimestamp && ( {parentLayer} Transaction Time}> - + )} @@ -231,13 +226,11 @@ function ClaimedTxInfo({ tx, isSourceChainArbitrum }: CommonProps) { const isExecuted = tx.status === 'Executed' const isBeingClaimed = tx.status === 'Confirmed' && tx.resolvedAt - const claimedTx = tx.isCctp - ? { - txId: tx.cctpData?.receiveMessageTransactionHash - } - : findMatchingL1TxForWithdrawal(tx) + const claimedTxId = tx.isCctp + ? tx.cctpData?.receiveMessageTransactionHash + : getWithdrawalClaimParentChainTxDetails(tx)?.txId - if (!claimedTx?.txId) { + if (!claimedTxId) { return ( To @@ -257,10 +250,10 @@ function ClaimedTxInfo({ tx, isSourceChainArbitrum }: CommonProps) { {getNetworkName(toNetworkId)}:{' '} - {shortenTxHash(claimedTx.txId)} + {shortenTxHash(claimedTxId)} ) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts index 88b339a5d1..600ac1c21d 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts @@ -15,7 +15,11 @@ import { WithdrawalStatus } from '../../state/app/state' import { ChainId, getBlockTime, isNetwork, rpcURLs } from '../../util/networks' -import { isCctpTransfer } from '../../hooks/useTransactionHistory' +import { + ChainPair, + Deposit, + isCctpTransfer +} from '../../hooks/useTransactionHistory' import { getWagmiChain } from '../../util/wagmi/getWagmiChain' import { getL1ToL2MessageDataFromL1TxHash } from '../../util/deposits/helpers' import { AssetType } from '../../hooks/arbTokenBridge.types' @@ -26,6 +30,10 @@ import { } from '../../state/cctpState' import { getAttestationHashAndMessageFromReceipt } from '../../util/cctp/getAttestationHashAndMessageFromReceipt' +const CLAIM_PARENT_CHAIN_DETAILS_LOCAL_STORAGE_KEY = + 'arbitrum:bridge:claim:parent:tx:details' +const DEPOSITS_LOCAL_STORAGE_KEY = 'arbitrum:bridge:deposits' + export enum StatusLabel { PENDING = 'Pending', CLAIMABLE = 'Claimable', @@ -114,6 +122,22 @@ export function isSameTransaction( ) } +export function subgraphExistsForChainPair(chainPair: ChainPair) { + if ( + chainPair.parentChain === ChainId.Ethereum && + chainPair.chain === ChainId.ArbitrumOne + ) { + return true + } + if ( + chainPair.parentChain === ChainId.Goerli && + chainPair.chain === ChainId.ArbitrumGoerli + ) { + return true + } + return false +} + export function getTxReceipt(tx: MergedTransaction) { const parentChainProvider = getProvider(tx.parentChainId) const childChainProvider = getProvider(tx.childChainId) @@ -136,6 +160,89 @@ function getWithdrawalStatusFromReceipt( } } +export function getDepositsWithoutStatusesFromCache(): Deposit[] { + return JSON.parse( + localStorage.getItem(DEPOSITS_LOCAL_STORAGE_KEY) ?? '[]' + ) as Deposit[] +} + +export function addDepositToCache(tx: Deposit) { + if (tx.direction !== 'deposit' || tx.source !== 'event_logs') { + return + } + + const cachedDeposits = getDepositsWithoutStatusesFromCache() + + const foundInCache = cachedDeposits.find(cachedTx => { + return ( + cachedTx.txID === tx.txID && + cachedTx.parentChainId === tx.parentChainId && + cachedTx.childChainId === tx.childChainId + ) + }) + + if (foundInCache) { + return + } + + const newCachedDeposits = [tx, ...cachedDeposits] + + localStorage.setItem( + DEPOSITS_LOCAL_STORAGE_KEY, + JSON.stringify(newCachedDeposits) + ) +} + +export function setWithdrawalClaimParentChainTxId( + tx: MergedTransaction, + parentChainTxId: string +) { + const key = `${tx.parentChainId}-${tx.childChainId}-${tx.txId}` + + const cachedClaimParentChainTxId = JSON.parse( + localStorage.getItem(CLAIM_PARENT_CHAIN_DETAILS_LOCAL_STORAGE_KEY) ?? '{}' + ) + + if (key in cachedClaimParentChainTxId) { + // already set + return + } + + localStorage.setItem( + CLAIM_PARENT_CHAIN_DETAILS_LOCAL_STORAGE_KEY, + JSON.stringify({ + ...cachedClaimParentChainTxId, + [key]: { + txId: parentChainTxId, + timestamp: dayjs().valueOf() + } + }) + ) +} + +export function getWithdrawalClaimParentChainTxDetails( + tx: MergedTransaction +): { txId: string; timestamp: number } | undefined { + if (!tx.isWithdrawal || tx.isCctp) { + return undefined + } + + const key = `${tx.parentChainId}-${tx.childChainId}-${tx.txId}` + + const cachedClaimParentChainTxDetails = ( + JSON.parse( + localStorage.getItem(CLAIM_PARENT_CHAIN_DETAILS_LOCAL_STORAGE_KEY) ?? '{}' + ) as { + [key in string]: { + txId: string + timestamp: number + } + } + )[key] + + return cachedClaimParentChainTxDetails +} + export async function getUpdatedEthDeposit( tx: MergedTransaction ): Promise { @@ -286,7 +393,6 @@ export async function getUpdatedCctpTransfer( const receipt = await getTxReceipt(tx) const l1SourceChain = getL1ChainIdFromSourceChain(tx) - console.log({ l1SourceChain }) const requiredL1BlocksBeforeConfirmation = getBlockBeforeConfirmation(l1SourceChain) const blockTime = getBlockTime(l1SourceChain) diff --git a/packages/arb-token-bridge-ui/src/components/syncers/RetryableTxnsIncluder.tsx b/packages/arb-token-bridge-ui/src/components/syncers/RetryableTxnsIncluder.tsx deleted file mode 100644 index 9ad5efabf2..0000000000 --- a/packages/arb-token-bridge-ui/src/components/syncers/RetryableTxnsIncluder.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { useCallback, useEffect } from 'react' -import { L1ToL2MessageStatus } from '@arbitrum/sdk' -import { - EthDepositMessage, - L1ToL2MessageReader, - L1ToL2MessageReaderClassic -} from '@arbitrum/sdk/dist/lib/message/L1ToL2Message' -import { AssetType } from '../../hooks/arbTokenBridge.types' -import { useActions, useAppState } from '../../state' -import { useInterval } from '../common/Hooks' -import { useNetworksAndSigners } from '../../hooks/useNetworksAndSigners' -import { getL1ToL2MessageDataFromL1TxHash } from '../../util/deposits/helpers' - -export function RetryableTxnsIncluder(): JSX.Element { - const actions = useActions() - const { - l1: { provider: l1Provider }, - l2: { provider: l2Provider } - } = useNetworksAndSigners() - - const { - app: { arbTokenBridge, arbTokenBridgeLoaded } - } = useAppState() - - const fetchAndUpdateDepositStatus = useCallback( - async (depositTxId: string, depositAssetType: AssetType | string) => { - const isEthDeposit = depositAssetType === AssetType.ETH - - const { l1ToL2Msg, isClassic } = await getL1ToL2MessageDataFromL1TxHash({ - depositTxId, - isEthDeposit, - l1Provider, - l2Provider - }) - - if (!l1ToL2Msg) return - - const status = await l1ToL2Msg?.status() - - // Classic messages - if (isClassic) { - arbTokenBridge?.transactions?.fetchAndUpdateL1ToL2MsgClassicStatus( - depositTxId, - l1ToL2Msg as L1ToL2MessageReaderClassic, - isEthDeposit, - status as L1ToL2MessageStatus - ) - return - } - - // Non-classic - Eth deposit - if (isEthDeposit) { - arbTokenBridge?.transactions?.fetchAndUpdateEthDepositMessageStatus( - depositTxId, - l1ToL2Msg as EthDepositMessage - ) - } else { - // Non-classic - Token deposit - arbTokenBridge.transactions?.fetchAndUpdateL1ToL2MsgStatus( - depositTxId, - l1ToL2Msg as L1ToL2MessageReader, - false, - status as L1ToL2MessageStatus - ) - } - }, - [l1Provider, l2Provider, arbTokenBridge?.transactions] - ) - - const checkAndUpdateFailedRetryables = useCallback(async () => { - const failedRetryablesToRedeem = actions.app.getFailedRetryablesToRedeem() - for (const depositTx of failedRetryablesToRedeem) { - const depositTxId = depositTx.txId - const depositAssetType = depositTx.asset - - fetchAndUpdateDepositStatus(depositTxId, depositAssetType) - } - }, [ - arbTokenBridge?.transactions?.addTransactions, - fetchAndUpdateDepositStatus - ]) - - /** - * For every L1 deposit, we ensure the relevant L1ToL2MessageIsIncluded - */ - const checkAndAddMissingL1ToL2Messages = useCallback(async () => { - const l1DepositsWithUntrackedL2Messages = - actions.app.l1DepositsWithUntrackedL2Messages() - - for (const depositTx of l1DepositsWithUntrackedL2Messages) { - const depositTxId = depositTx.txID - const depositAssetType = depositTx.assetType - - fetchAndUpdateDepositStatus(depositTxId, depositAssetType) - } - }, [ - arbTokenBridge?.transactions?.addTransactions, - fetchAndUpdateDepositStatus - ]) - - const { forceTrigger: forceTriggerUpdate } = useInterval( - checkAndAddMissingL1ToL2Messages, - 5000 - ) - - const { forceTrigger: forceTriggerUpdateFailedRetryables } = useInterval( - checkAndUpdateFailedRetryables, - 10000 - ) - - useEffect(() => { - // force trigger update each time loaded change happens - forceTriggerUpdate() - forceTriggerUpdateFailedRetryables() - }, [arbTokenBridgeLoaded]) - - return <> -} diff --git a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts index d024dae8df..817c4faea3 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts @@ -46,6 +46,10 @@ import { useUpdateUSDCBalances } from './CCTP/useUpdateUSDCBalances' import { useNativeCurrency } from './useNativeCurrency' import { useTransactionHistory } from './useTransactionHistory' import { DepositStatus, WithdrawalStatus } from '../state/app/state' +import { + addDepositToCache, + subgraphExistsForChainPair +} from '../components/TransactionHistory/helpers' export const wait = (ms = 0) => { return new Promise(res => setTimeout(res, ms)) @@ -227,6 +231,29 @@ export const useArbTokenBridge = ( childChainId: Number(l2NetworkID) }) + addDepositToCache({ + sender: walletAddress, + destination: walletAddress, + status: 'pending', + txID: tx.hash, + assetName: nativeCurrency.symbol, + assetType: AssetType.ETH, + l1NetworkID, + l2NetworkID, + value: utils.formatUnits(amount, nativeCurrency.decimals), + parentChainId: Number(l1NetworkID), + childChainId: Number(l2NetworkID), + direction: 'deposit', + type: 'deposit-l1', + source: subgraphExistsForChainPair({ + parentChain: Number(l1NetworkID), + chain: Number(l2NetworkID) + }) + ? 'subgraph' + : 'event_logs', + timestampCreated: String(dayjs().valueOf() / 1_000) + }) + const receipt = await tx.wait() if (txLifecycle?.onTxConfirm) { @@ -443,6 +470,29 @@ export const useArbTokenBridge = ( childChainId: Number(l2NetworkID) }) + addDepositToCache({ + sender: walletAddress, + destination: walletAddress, + status: 'pending', + txID: tx.hash, + assetName: symbol, + assetType: AssetType.ERC20, + l1NetworkID, + l2NetworkID, + value: utils.formatUnits(amount, nativeCurrency.decimals), + parentChainId: Number(l1NetworkID), + childChainId: Number(l2NetworkID), + direction: 'deposit', + type: 'deposit-l1', + source: subgraphExistsForChainPair({ + parentChain: Number(l1NetworkID), + chain: Number(l2NetworkID) + }) + ? 'subgraph' + : 'event_logs', + timestampCreated: String(dayjs().valueOf() / 1_000) + }) + const receipt = await tx.wait() if (txLifecycle?.onTxConfirm) { diff --git a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts index 92c62c2b79..8749c3cbd9 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts @@ -7,7 +7,10 @@ import { MergedTransaction, WithdrawalStatus } from '../state/app/state' import { isUserRejectedError } from '../util/isUserRejectedError' import { errorToast } from '../components/common/atoms/Toast' import { AssetType, L2ToL1EventResultPlus } from './arbTokenBridge.types' -import { getProvider } from '../components/TransactionHistory/helpers' +import { + getProvider, + setWithdrawalClaimParentChainTxId +} from '../components/TransactionHistory/helpers' import { L2TransactionReceipt } from '@arbitrum/sdk' import { ContractReceipt, utils } from 'ethers' import { useTransactionHistory } from './useTransactionHistory' @@ -108,12 +111,17 @@ export function useClaimWithdrawal(): UseClaimWithdrawalResult { } const isSuccess = (res as ContractReceipt).status === 1 + const txHash = (res as ContractReceipt).transactionHash updatePendingTransaction({ ...tx, status: isSuccess ? WithdrawalStatus.EXECUTED : WithdrawalStatus.FAILURE, resolvedAt: isSuccess ? dayjs().valueOf() : null }) + + if (isSuccess) { + setWithdrawalClaimParentChainTxId(tx, txHash) + } } return { claim, isClaiming } diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index a89009fa19..6e78dc1143 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -35,6 +35,7 @@ import { FetchWithdrawalsFromSubgraphResult } from '../util/withdrawals/fetchWit import { updateAdditionalDepositData } from '../util/deposits/helpers' import { useCctpFetching } from '../state/cctpState' import { + getDepositsWithoutStatusesFromCache, getProvider, getUpdatedCctpTransfer, getUpdatedEthDeposit, @@ -55,7 +56,7 @@ export type Withdrawal = type DepositOrWithdrawal = Deposit | Withdrawal type Transfer = DepositOrWithdrawal | MergedTransaction -type ChainPair = { parentChain: ChainId; chain: ChainId } +export type ChainPair = { parentChain: ChainId; chain: ChainId } export type TransactionHistoryParams = { data: { @@ -304,7 +305,11 @@ const useTransactionHistoryWithoutStatuses = ( () => fetcher('withdrawals') ) - const deposits = (depositsData || []).flat() + const deposits = [ + (depositsData || []).flat(), + ...getDepositsWithoutStatusesFromCache() + ] + const withdrawals = (withdrawalsData || []).flat() // merge deposits and withdrawals and sort them by date @@ -568,6 +573,20 @@ export const useTransactionHistory = ( return Math.max(daysAgo, 1) }, [transactions]) + // duplicates may occur when txs are taken from the local storage + const dedupedTransactions = useMemo( + () => + Array.from( + new Map( + transactions.map(tx => [ + `${tx.parentChainId}-${tx.childChainId}-${tx.txId}}`, + tx + ]) + ).values() + ), + [transactions] + ) + function pause() { setFetching(false) } @@ -592,11 +611,11 @@ export const useTransactionHistory = ( return { data: { - transactions, + transactions: dedupedTransactions, numberOfDays: oldestTransactionDaysAgo }, loading: fetching, - completed: transactions.length === data.length, + completed: dedupedTransactions.length >= data.length, error: txPagesError ?? error, pause, resume, diff --git a/packages/arb-token-bridge-ui/src/state/app/utils.ts b/packages/arb-token-bridge-ui/src/state/app/utils.ts index b3ef70a595..74d87cf7ff 100644 --- a/packages/arb-token-bridge-ui/src/state/app/utils.ts +++ b/packages/arb-token-bridge-ui/src/state/app/utils.ts @@ -240,30 +240,3 @@ export const getStandardizedTime = (standardizedTimestamp: number) => { export const getStandardizedDate = (standardizedTimestamp: number) => { return dayjs(standardizedTimestamp).format(TX_DATE_FORMAT) // dayjs timestamp -> date } - -export const findMatchingL1TxForWithdrawal = ( - withdrawalTxn: MergedTransaction -) => { - // finds the corresponding L1 transaction for withdrawal - - const cachedTransactions: Transaction[] = JSON.parse( - window.localStorage.getItem('arbTransactions') || '[]' - ) - const outboxTransactions = cachedTransactions - .filter(tx => tx.type === 'outbox') - .map(transformDeposit) - - return outboxTransactions.find(_tx => { - const l2ToL1MsgData = _tx.l2ToL1MsgData - - if (!(l2ToL1MsgData?.uniqueId && withdrawalTxn?.uniqueId)) { - return false - } - - // To get rid of Proxy - const txUniqueId = BigNumber.from(withdrawalTxn.uniqueId) - const _txUniqueId = BigNumber.from(l2ToL1MsgData.uniqueId) - - return txUniqueId.eq(_txUniqueId) - }) -} From 11812a936eaa93e24ea6c31f9c5ea57186651c7e Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 20 Dec 2023 11:59:14 +0100 Subject: [PATCH 57/72] fix --- .../src/hooks/useTransactionHistory.ts | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 19f8839f79..d254806c62 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -67,22 +67,6 @@ export type Withdrawal = type DepositOrWithdrawal = Deposit | Withdrawal type Transfer = DepositOrWithdrawal | MergedTransaction -type ChainPair = { parentChain: ChainId; chain: ChainId } - -export type TransactionHistoryParams = { - data: { - transactions: MergedTransaction[] - numberOfDays: number - } - loading: boolean - completed: boolean - error: unknown - pause: () => void - resume: () => void - addPendingTransaction: (tx: MergedTransaction) => void - updatePendingTransaction: (tx: MergedTransaction) => void -} - function getStandardizedTimestampByTx(tx: Transfer) { if (isCctpTransfer(tx)) { return (tx.createdAt ?? 0) / 1_000 @@ -105,7 +89,7 @@ function sortByTimestampDescending(a: Transfer, b: Transfer) { : 1 } -const multiChainFetchList: ChainPair[] = [ +const multiChainFetchList: { parentChain: ChainId; chain: ChainId }[] = [ { parentChain: ChainId.Ethereum, chain: ChainId.ArbitrumOne From 6fb25d8df234b888142ea1988f1d6050397b22fe Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 20 Dec 2023 12:18:57 +0100 Subject: [PATCH 58/72] comment out sepolia, fixes --- .../src/components/common/SidePanel.tsx | 69 +++++-------------- .../src/hooks/useTransactionHistory.ts | 16 ++--- 2 files changed, 27 insertions(+), 58 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/common/SidePanel.tsx b/packages/arb-token-bridge-ui/src/components/common/SidePanel.tsx index 9a2b00ca34..4d8591773b 100644 --- a/packages/arb-token-bridge-ui/src/components/common/SidePanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/common/SidePanel.tsx @@ -1,4 +1,3 @@ -import { useState } from 'react' import { Dialog } from '@headlessui/react' import { twMerge } from 'tailwind-merge' import { XMarkIcon } from '@heroicons/react/24/outline' @@ -24,22 +23,11 @@ export const SidePanel = ({ }: SidePanelProps) => { const ANIMATION_DURATION = 300 - const [isClosing, setIsClosing] = useState(false) - const [isAnimating, setIsAnimating] = useState(true) - - const handleOnClose = () => { - setIsClosing(true) - onClose?.() - setTimeout(() => { - setIsClosing(false) - }, ANIMATION_DURATION) // Matches the transition duration - } - return ( - + onClose?.()} className="fixed z-50 h-screen max-h-screen" > {/* The backdrop, rendered as a fixed sibling to the panel container */} @@ -59,44 +47,25 @@ export const SidePanel = ({ {/* Full-screen container to center the panel */} -
+
{/* The heading of dialog */} - setIsAnimating(true)} - onAnimationEnd={() => setIsAnimating(false)} - enter={`transition-transform duration-${ANIMATION_DURATION}`} - enterFrom="transform translate-x-full" - enterTo="transform translate-x-0" - leave={`transition-transform duration-${ANIMATION_DURATION}`} - leaveFrom="transform translate-x-0" - leaveTo="transform translate-x-full" - className="h-full" + - - - {heading && {heading}} - - + + {heading && {heading}} + + - {/* Contents of the panel */} -
- {children} -
-
-
+ {/* Contents of the panel */} +
{children}
+
diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index d254806c62..748f2a880a 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -103,19 +103,19 @@ const multiChainFetchList: { parentChain: ChainId; chain: ChainId }[] = [ parentChain: ChainId.Goerli, chain: ChainId.ArbitrumGoerli }, - { - parentChain: ChainId.Sepolia, - chain: ChainId.ArbitrumSepolia - }, + // { + // parentChain: ChainId.Sepolia, + // chain: ChainId.ArbitrumSepolia + // }, // Orbit { parentChain: ChainId.ArbitrumGoerli, chain: ChainId.XaiTestnet }, - { - parentChain: ChainId.ArbitrumSepolia, - chain: ChainId.StylusTestnet - }, + // { + // parentChain: ChainId.ArbitrumSepolia, + // chain: ChainId.StylusTestnet + // }, ...getCustomChainsFromLocalStorage().map(chain => { return { parentChain: chain.partnerChainID, From 56de752d97bb39f9ae0a41926796369756e001cf Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 20 Dec 2023 12:29:46 +0100 Subject: [PATCH 59/72] fix --- packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts b/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts index 4b66f598ad..02b890ec75 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts @@ -3,9 +3,10 @@ import { useLocalStorage } from 'react-use' const testnetModeLocalStorageKey = 'arbitrum:bridge:settings:testnetMode' export const useIsTestnetMode = () => { - const [isTestnetMode = false, setIsTestnetMode] = useLocalStorage( - testnetModeLocalStorageKey + const [isTestnetMode, setIsTestnetMode] = useLocalStorage( + testnetModeLocalStorageKey, + false ) - return [isTestnetMode, setIsTestnetMode] + return [isTestnetMode, setIsTestnetMode] as const } From e30ce4d4b5e6576227d759d7eeee813161b06a9b Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 20 Dec 2023 12:31:40 +0100 Subject: [PATCH 60/72] clean up --- .../components/TransactionHistory/TransactionHistoryTable.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx index f350ed89bd..c8ec0e96f8 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx @@ -129,7 +129,7 @@ export const TransactionHistoryTable = ({
0 ? 'rounded-tl-lg' : '' + isPendingTab ? '' : 'rounded-tl-lg' )} > @@ -203,7 +203,7 @@ export const TransactionHistoryTable = ({
0 ? 'rounded-tl-lg' : '' + isPendingTab ? '' : 'rounded-tl-lg' )} > {loading ? ( From bdb774693cfea2038a8f4a7b51a4af1ef784a659 Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 20 Dec 2023 12:44:49 +0100 Subject: [PATCH 61/72] clean up --- .../src/components/TransactionHistory/helpers.ts | 13 ++++++++++++- .../src/hooks/useClaimWithdrawal.ts | 4 ++-- .../src/hooks/useTransactionHistory.ts | 4 +++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts index 7474572112..023881dc0b 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts @@ -163,6 +163,11 @@ export function getDepositsWithoutStatusesFromCache(): Deposit[] { ) as Deposit[] } +/** + * Cache deposits from event logs. We don't fetch these so we need to store them locally. + * + * @param {MergedTransaction} tx - Deposit from event logs to be cached. + */ export function addDepositToCache(tx: Deposit) { if (tx.direction !== 'deposit' || tx.source !== 'event_logs') { return @@ -190,7 +195,13 @@ export function addDepositToCache(tx: Deposit) { ) } -export function setWithdrawalClaimParentChainTxId( +/** + * Cache parent chain tx details when claiming. This is the chain the funds were claimed on. We store locally because we don't have access to this tx from the child chain tx data. + * + * @param {MergedTransaction} tx - Transaction that initiated the withdrawal (child chain transaction). + * @param {string} parentChainTxId - Transaction ID of the claim transaction (parent chain transaction ID). + */ +export function setWithdrawalClaimParentChainTxDetails( tx: MergedTransaction, parentChainTxId: string ) { diff --git a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts index b73df803a0..bb80711a7c 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts @@ -9,7 +9,7 @@ import { errorToast } from '../components/common/atoms/Toast' import { AssetType, L2ToL1EventResultPlus } from './arbTokenBridge.types' import { getProvider, - setWithdrawalClaimParentChainTxId + setWithdrawalClaimParentChainTxDetails } from '../components/TransactionHistory/helpers' import { L2TransactionReceipt } from '@arbitrum/sdk' import { ContractReceipt, utils } from 'ethers' @@ -121,7 +121,7 @@ export function useClaimWithdrawal(): UseClaimWithdrawalResult { }) if (isSuccess) { - setWithdrawalClaimParentChainTxId(tx, txHash) + setWithdrawalClaimParentChainTxDetails(tx, txHash) } } diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 72899dea73..15e1bd03f9 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -58,6 +58,8 @@ export type TransactionHistoryParams = { updatePendingTransaction: (tx: MergedTransaction) => void } +export type ChainPair = { parentChain: ChainId; chain: ChainId } + export type Deposit = Transaction export type Withdrawal = @@ -90,7 +92,7 @@ function sortByTimestampDescending(a: Transfer, b: Transfer) { : 1 } -const multiChainFetchList: { parentChain: ChainId; chain: ChainId }[] = [ +const multiChainFetchList: ChainPair[] = [ { parentChain: ChainId.Ethereum, chain: ChainId.ArbitrumOne From c95a5231a72d9c2d547f8f6090b2b637243905eb Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 20 Dec 2023 15:33:14 +0100 Subject: [PATCH 62/72] add nonce --- packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts | 6 ++++-- packages/arb-token-bridge-ui/src/hooks/useTransactions.ts | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts index cff3fc0e23..0ad3ce8a24 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts @@ -251,7 +251,8 @@ export const useArbTokenBridge = ( }) ? 'subgraph' : 'event_logs', - timestampCreated: String(dayjs().valueOf() / 1_000) + timestampCreated: String(dayjs().valueOf() / 1_000), + nonce: tx.nonce }) const receipt = await tx.wait() @@ -490,7 +491,8 @@ export const useArbTokenBridge = ( }) ? 'subgraph' : 'event_logs', - timestampCreated: String(dayjs().valueOf() / 1_000) + timestampCreated: String(dayjs().valueOf() / 1_000), + nonce: tx.nonce }) const receipt = await tx.wait() diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactions.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactions.ts index 873fee9d92..740d3f82ed 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactions.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactions.ts @@ -105,6 +105,7 @@ export interface Transaction extends TransactionBase { source: 'subgraph' | 'event_logs' parentChainId: number childChainId: number + nonce?: number } export interface NewTransaction extends TransactionBase { From 09e3e80647b717bee12d1dc48ab9564a6e53f911 Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 20 Dec 2023 17:05:45 +0100 Subject: [PATCH 63/72] changes --- .../components/TransactionHistory/helpers.ts | 45 ++++++++----------- .../src/hooks/arbTokenBridge.types.ts | 2 +- .../src/hooks/useArbTokenBridge.ts | 37 +++++++-------- .../src/hooks/useTransactionHistory.ts | 12 +++-- .../src/hooks/useTransactions.ts | 2 +- .../fetchWithdrawalsFromSubgraph.ts | 2 +- .../src/util/withdrawals/helpers.ts | 2 +- 7 files changed, 46 insertions(+), 56 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts index 023881dc0b..7f8067e81d 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts @@ -109,32 +109,24 @@ export function getProvider(chainId: ChainId) { } export function isSameTransaction( - tx_1: MergedTransaction, - tx_2: MergedTransaction + txDetails_1: { + txId: string + parentChainId: ChainId + childChainId: ChainId + }, + txDetails_2: { + txId: string + parentChainId: ChainId + childChainId: ChainId + } ) { return ( - tx_1.txId === tx_2.txId && - tx_1.parentChainId === tx_2.parentChainId && - tx_1.childChainId === tx_2.childChainId + txDetails_1.txId === txDetails_2.txId && + txDetails_1.parentChainId === txDetails_2.parentChainId && + txDetails_1.childChainId === txDetails_2.childChainId ) } -export function subgraphExistsForChainPair(chainPair: ChainPair) { - if ( - chainPair.parentChain === ChainId.Ethereum && - chainPair.chain === ChainId.ArbitrumOne - ) { - return true - } - if ( - chainPair.parentChain === ChainId.Goerli && - chainPair.chain === ChainId.ArbitrumGoerli - ) { - return true - } - return false -} - export function getTxReceipt(tx: MergedTransaction) { const parentChainProvider = getProvider(tx.parentChainId) const childChainProvider = getProvider(tx.childChainId) @@ -175,13 +167,12 @@ export function addDepositToCache(tx: Deposit) { const cachedDeposits = getDepositsWithoutStatusesFromCache() - const foundInCache = cachedDeposits.find(cachedTx => { - return ( - cachedTx.txID === tx.txID && - cachedTx.parentChainId === tx.parentChainId && - cachedTx.childChainId === tx.childChainId + const foundInCache = cachedDeposits.find(cachedTx => + isSameTransaction( + { ...cachedTx, txId: cachedTx.txID }, + { ...tx, txId: tx.txID } ) - }) + ) if (foundInCache) { return diff --git a/packages/arb-token-bridge-ui/src/hooks/arbTokenBridge.types.ts b/packages/arb-token-bridge-ui/src/hooks/arbTokenBridge.types.ts index f76249e7b9..80d24dcfa2 100644 --- a/packages/arb-token-bridge-ui/src/hooks/arbTokenBridge.types.ts +++ b/packages/arb-token-bridge-ui/src/hooks/arbTokenBridge.types.ts @@ -97,7 +97,7 @@ export type WithdrawalInitiated = EventArgs & { txHash: string timestamp?: BigNumber direction: 'deposit' | 'withdrawal' - source: 'subgraph' | 'event_logs' + source: 'subgraph' | 'event_logs' | 'local_storage_cache' parentChainId: number childChainId: number } diff --git a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts index 0ad3ce8a24..8546d2f27a 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts @@ -46,10 +46,7 @@ import { useUpdateUSDCBalances } from './CCTP/useUpdateUSDCBalances' import { useNativeCurrency } from './useNativeCurrency' import { useTransactionHistory } from './useTransactionHistory' import { DepositStatus, WithdrawalStatus } from '../state/app/state' -import { - addDepositToCache, - subgraphExistsForChainPair -} from '../components/TransactionHistory/helpers' +import { addDepositToCache } from '../components/TransactionHistory/helpers' export const wait = (ms = 0) => { return new Promise(res => setTimeout(res, ms)) @@ -192,6 +189,9 @@ export const useArbTokenBridge = ( } const ethBridger = await EthBridger.fromProvider(l2.provider) + const parentChainBlockTimestamp = Math.floor( + (await l1.provider.getBlock('latest')).timestamp + ) let tx: L1EthDepositTransaction @@ -216,7 +216,7 @@ export const useArbTokenBridge = ( destination: walletAddress, direction: 'deposit-l1', status: 'pending', - createdAt: dayjs().valueOf(), + createdAt: parentChainBlockTimestamp, resolvedAt: null, txId: tx.hash, asset: nativeCurrency.symbol, @@ -245,13 +245,8 @@ export const useArbTokenBridge = ( childChainId: Number(l2NetworkID), direction: 'deposit', type: 'deposit-l1', - source: subgraphExistsForChainPair({ - parentChain: Number(l1NetworkID), - chain: Number(l2NetworkID) - }) - ? 'subgraph' - : 'event_logs', - timestampCreated: String(dayjs().valueOf() / 1_000), + source: 'local_storage_cache', + timestampCreated: String(parentChainBlockTimestamp), nonce: tx.nonce }) @@ -426,6 +421,9 @@ export const useArbTokenBridge = ( return } const erc20Bridger = await Erc20Bridger.fromProvider(l2.provider) + const parentChainBlockTimestamp = Math.floor( + (await l1.provider.getBlock('latest')).timestamp + ) try { const { symbol, decimals } = await fetchErc20Data({ @@ -456,7 +454,7 @@ export const useArbTokenBridge = ( destination: destinationAddress ?? walletAddress, direction: 'deposit-l1', status: 'pending', - createdAt: dayjs().valueOf(), + createdAt: parentChainBlockTimestamp, resolvedAt: null, txId: tx.hash, asset: symbol, @@ -473,25 +471,20 @@ export const useArbTokenBridge = ( addDepositToCache({ sender: walletAddress, - destination: walletAddress, + destination: destinationAddress ?? walletAddress, status: 'pending', txID: tx.hash, assetName: symbol, assetType: AssetType.ERC20, l1NetworkID, l2NetworkID, - value: utils.formatUnits(amount, nativeCurrency.decimals), + value: utils.formatUnits(amount, decimals), parentChainId: Number(l1NetworkID), childChainId: Number(l2NetworkID), direction: 'deposit', type: 'deposit-l1', - source: subgraphExistsForChainPair({ - parentChain: Number(l1NetworkID), - chain: Number(l2NetworkID) - }) - ? 'subgraph' - : 'event_logs', - timestampCreated: String(dayjs().valueOf() / 1_000), + source: 'local_storage_cache', + timestampCreated: String(parentChainBlockTimestamp), nonce: tx.nonce }) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 15e1bd03f9..3daf188524 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -289,8 +289,8 @@ const useTransactionHistoryWithoutStatuses = ( ) const deposits = [ - (depositsData || []).flat(), - ...getDepositsWithoutStatusesFromCache() + ...getDepositsWithoutStatusesFromCache(), + (depositsData || []).flat() ] const withdrawals = (withdrawalsData || []).flat() @@ -605,13 +605,19 @@ export const useTransactionHistory = ( } } + if (dedupedTransactions.length > data.length) { + throw new Error( + `Unexpected number of deduped transactions, expected lte ${data.length}, got ${dedupedTransactions.length}.` + ) + } + return { data: { transactions: dedupedTransactions, numberOfDays: oldestTransactionDaysAgo }, loading: fetching, - completed: dedupedTransactions.length >= data.length, + completed: dedupedTransactions.length === data.length, error: txPagesError ?? error, pause, resume, diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactions.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactions.ts index 740d3f82ed..3c3f274211 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactions.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactions.ts @@ -102,7 +102,7 @@ type TransactionBase = { export interface Transaction extends TransactionBase { txID: string direction: 'deposit' | 'withdrawal' - source: 'subgraph' | 'event_logs' + source: 'subgraph' | 'event_logs' | 'local_storage_cache' parentChainId: number childChainId: number nonce?: number diff --git a/packages/arb-token-bridge-ui/src/util/withdrawals/fetchWithdrawalsFromSubgraph.ts b/packages/arb-token-bridge-ui/src/util/withdrawals/fetchWithdrawalsFromSubgraph.ts index eff89f9d01..5910ed19f7 100644 --- a/packages/arb-token-bridge-ui/src/util/withdrawals/fetchWithdrawalsFromSubgraph.ts +++ b/packages/arb-token-bridge-ui/src/util/withdrawals/fetchWithdrawalsFromSubgraph.ts @@ -15,7 +15,7 @@ export type FetchWithdrawalsFromSubgraphResult = { l2TxHash: string l2BlockNum: string direction: 'deposit' | 'withdrawal' - source: 'subgraph' | 'event_logs' + source: 'subgraph' | 'event_logs' | 'local_storage_cache' parentChainId: number childChainId: number } diff --git a/packages/arb-token-bridge-ui/src/util/withdrawals/helpers.ts b/packages/arb-token-bridge-ui/src/util/withdrawals/helpers.ts index eb35961bc6..5f08c1b1bb 100644 --- a/packages/arb-token-bridge-ui/src/util/withdrawals/helpers.ts +++ b/packages/arb-token-bridge-ui/src/util/withdrawals/helpers.ts @@ -24,7 +24,7 @@ export type EthWithdrawal = L2ToL1EventResult & { l2TxHash?: string transactionHash?: string direction: 'deposit' | 'withdrawal' - source: 'subgraph' | 'event_logs' + source: 'subgraph' | 'event_logs' | 'local_storage_cache' parentChainId: number childChainId: number } From 8a2b2e14393f99b85633a7a73fb7016a8dca425a Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 20 Dec 2023 17:23:17 +0100 Subject: [PATCH 64/72] fixes --- .../src/hooks/useTransactionHistory.ts | 66 +++++++++++-------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 3daf188524..e4516716c3 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -197,6 +197,22 @@ function getTransactionsMapKey(tx: MergedTransaction) { return `${tx.parentChainId}-${tx.childChainId}-${tx.txId}` } +function getTxIdFromTransaction(tx: Transfer) { + if (isCctpTransfer(tx)) { + return tx.txId + } + if (isDeposit(tx)) { + return tx.txID + } + if (isWithdrawalFromSubgraph(tx)) { + return tx.l2TxHash + } + if (isTokenWithdrawal(tx)) { + return tx.txHash + } + return tx.l2TxHash +} + /** * Fetches transaction history only for deposits and withdrawals, without their statuses. */ @@ -296,12 +312,30 @@ const useTransactionHistoryWithoutStatuses = ( const withdrawals = (withdrawalsData || []).flat() // merge deposits and withdrawals and sort them by date - const transactions = [...deposits, ...withdrawals, ...combinedCctpTransfers] - .flat() - .sort(sortByTimestampDescending) + const transactions = [ + ...deposits, + ...withdrawals, + ...combinedCctpTransfers + ].flat() + + // duplicates may occur when txs are taken from the local storage + const dedupedTransactions = useMemo( + () => + Array.from( + new Map( + transactions.map(tx => [ + `${tx.parentChainId}-${tx.childChainId}-${getTxIdFromTransaction( + tx + )}}`, + tx + ]) + ).values() + ).sort(sortByTimestampDescending), + [transactions] + ) return { - data: transactions, + data: dedupedTransactions, loading: depositsLoading || withdrawalsLoading || cctpLoading, error: depositsError ?? withdrawalsError } @@ -569,20 +603,6 @@ export const useTransactionHistory = ( return Math.max(daysAgo, 1) }, [transactions]) - // duplicates may occur when txs are taken from the local storage - const dedupedTransactions = useMemo( - () => - Array.from( - new Map( - transactions.map(tx => [ - `${tx.parentChainId}-${tx.childChainId}-${tx.txId}}`, - tx - ]) - ).values() - ), - [transactions] - ) - function pause() { setFetching(false) } @@ -605,19 +625,13 @@ export const useTransactionHistory = ( } } - if (dedupedTransactions.length > data.length) { - throw new Error( - `Unexpected number of deduped transactions, expected lte ${data.length}, got ${dedupedTransactions.length}.` - ) - } - return { data: { - transactions: dedupedTransactions, + transactions, numberOfDays: oldestTransactionDaysAgo }, loading: fetching, - completed: dedupedTransactions.length === data.length, + completed: transactions.length === data.length, error: txPagesError ?? error, pause, resume, From fb6347ea04979dc6c744b317abdcb98f4be9724a Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 20 Dec 2023 17:34:05 +0100 Subject: [PATCH 65/72] fix --- .../src/components/TransactionHistory/helpers.ts | 2 +- packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts index 7f8067e81d..79ddfd4d64 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts @@ -161,7 +161,7 @@ export function getDepositsWithoutStatusesFromCache(): Deposit[] { * @param {MergedTransaction} tx - Deposit from event logs to be cached. */ export function addDepositToCache(tx: Deposit) { - if (tx.direction !== 'deposit' || tx.source !== 'event_logs') { + if (tx.direction !== 'deposit') { return } diff --git a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts index 8546d2f27a..a85b77ab40 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts @@ -216,7 +216,7 @@ export const useArbTokenBridge = ( destination: walletAddress, direction: 'deposit-l1', status: 'pending', - createdAt: parentChainBlockTimestamp, + createdAt: parentChainBlockTimestamp * 1_000, resolvedAt: null, txId: tx.hash, asset: nativeCurrency.symbol, @@ -454,7 +454,7 @@ export const useArbTokenBridge = ( destination: destinationAddress ?? walletAddress, direction: 'deposit-l1', status: 'pending', - createdAt: parentChainBlockTimestamp, + createdAt: parentChainBlockTimestamp * 1_000, resolvedAt: null, txId: tx.hash, asset: symbol, From 1e5c0437a224f4d93c041bd42be94608bb1a44a3 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 21 Dec 2023 12:50:44 +0100 Subject: [PATCH 66/72] address comments --- .../src/components/TransactionHistory/helpers.ts | 16 ++++++---------- .../src/hooks/useArbTokenBridge.ts | 10 ++++------ .../src/hooks/useClaimWithdrawal.ts | 4 ++-- .../src/hooks/useTransactionHistory.ts | 2 +- .../withdrawals/fetchWithdrawalsFromSubgraph.ts | 2 +- 5 files changed, 14 insertions(+), 20 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts index 79ddfd4d64..f26eb1fbf1 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts @@ -15,11 +15,7 @@ import { WithdrawalStatus } from '../../state/app/state' import { ChainId, getBlockTime, isNetwork, rpcURLs } from '../../util/networks' -import { - ChainPair, - Deposit, - isCctpTransfer -} from '../../hooks/useTransactionHistory' +import { Deposit, isCctpTransfer } from '../../hooks/useTransactionHistory' import { getWagmiChain } from '../../util/wagmi/getWagmiChain' import { getL1ToL2MessageDataFromL1TxHash } from '../../util/deposits/helpers' import { AssetType } from '../../hooks/arbTokenBridge.types' @@ -27,7 +23,7 @@ import { getDepositStatus } from '../../state/app/utils' import { getBlockBeforeConfirmation } from '../../state/cctpState' import { getAttestationHashAndMessageFromReceipt } from '../../util/cctp/getAttestationHashAndMessageFromReceipt' -const CLAIM_PARENT_CHAIN_DETAILS_LOCAL_STORAGE_KEY = +const PARENT_CHAIN_TX_DETAILS_OF_CLAIM_TX = 'arbitrum:bridge:claim:parent:tx:details' const DEPOSITS_LOCAL_STORAGE_KEY = 'arbitrum:bridge:deposits' @@ -192,14 +188,14 @@ export function addDepositToCache(tx: Deposit) { * @param {MergedTransaction} tx - Transaction that initiated the withdrawal (child chain transaction). * @param {string} parentChainTxId - Transaction ID of the claim transaction (parent chain transaction ID). */ -export function setWithdrawalClaimParentChainTxDetails( +export function setParentChainTxDetailsOfWithdrawalClaimTx( tx: MergedTransaction, parentChainTxId: string ) { const key = `${tx.parentChainId}-${tx.childChainId}-${tx.txId}` const cachedClaimParentChainTxId = JSON.parse( - localStorage.getItem(CLAIM_PARENT_CHAIN_DETAILS_LOCAL_STORAGE_KEY) ?? '{}' + localStorage.getItem(PARENT_CHAIN_TX_DETAILS_OF_CLAIM_TX) ?? '{}' ) if (key in cachedClaimParentChainTxId) { @@ -208,7 +204,7 @@ export function setWithdrawalClaimParentChainTxDetails( } localStorage.setItem( - CLAIM_PARENT_CHAIN_DETAILS_LOCAL_STORAGE_KEY, + PARENT_CHAIN_TX_DETAILS_OF_CLAIM_TX, JSON.stringify({ ...cachedClaimParentChainTxId, [key]: { @@ -230,7 +226,7 @@ export function getWithdrawalClaimParentChainTxDetails( const cachedClaimParentChainTxDetails = ( JSON.parse( - localStorage.getItem(CLAIM_PARENT_CHAIN_DETAILS_LOCAL_STORAGE_KEY) ?? '{}' + localStorage.getItem(PARENT_CHAIN_TX_DETAILS_OF_CLAIM_TX) ?? '{}' ) as { [key in string]: { txId: string diff --git a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts index a85b77ab40..b33c427a6c 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts @@ -189,9 +189,8 @@ export const useArbTokenBridge = ( } const ethBridger = await EthBridger.fromProvider(l2.provider) - const parentChainBlockTimestamp = Math.floor( - (await l1.provider.getBlock('latest')).timestamp - ) + const parentChainBlockTimestamp = (await l1.provider.getBlock('latest')) + .timestamp let tx: L1EthDepositTransaction @@ -421,9 +420,8 @@ export const useArbTokenBridge = ( return } const erc20Bridger = await Erc20Bridger.fromProvider(l2.provider) - const parentChainBlockTimestamp = Math.floor( - (await l1.provider.getBlock('latest')).timestamp - ) + const parentChainBlockTimestamp = (await l1.provider.getBlock('latest')) + .timestamp try { const { symbol, decimals } = await fetchErc20Data({ diff --git a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts index bb80711a7c..c4ae3a54f1 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts @@ -9,7 +9,7 @@ import { errorToast } from '../components/common/atoms/Toast' import { AssetType, L2ToL1EventResultPlus } from './arbTokenBridge.types' import { getProvider, - setWithdrawalClaimParentChainTxDetails + setParentChainTxDetailsOfWithdrawalClaimTx } from '../components/TransactionHistory/helpers' import { L2TransactionReceipt } from '@arbitrum/sdk' import { ContractReceipt, utils } from 'ethers' @@ -121,7 +121,7 @@ export function useClaimWithdrawal(): UseClaimWithdrawalResult { }) if (isSuccess) { - setWithdrawalClaimParentChainTxDetails(tx, txHash) + setParentChainTxDetailsOfWithdrawalClaimTx(tx, txHash) } } diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index e4516716c3..762962be26 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -326,7 +326,7 @@ const useTransactionHistoryWithoutStatuses = ( transactions.map(tx => [ `${tx.parentChainId}-${tx.childChainId}-${getTxIdFromTransaction( tx - )}}`, + )?.toLowerCase()}}`, tx ]) ).values() diff --git a/packages/arb-token-bridge-ui/src/util/withdrawals/fetchWithdrawalsFromSubgraph.ts b/packages/arb-token-bridge-ui/src/util/withdrawals/fetchWithdrawalsFromSubgraph.ts index 5910ed19f7..9b649ad3ad 100644 --- a/packages/arb-token-bridge-ui/src/util/withdrawals/fetchWithdrawalsFromSubgraph.ts +++ b/packages/arb-token-bridge-ui/src/util/withdrawals/fetchWithdrawalsFromSubgraph.ts @@ -15,7 +15,7 @@ export type FetchWithdrawalsFromSubgraphResult = { l2TxHash: string l2BlockNum: string direction: 'deposit' | 'withdrawal' - source: 'subgraph' | 'event_logs' | 'local_storage_cache' + source: 'subgraph' parentChainId: number childChainId: number } From 85e745a331a5cf148c053313cd4855e86585a8bc Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 21 Dec 2023 13:33:41 +0100 Subject: [PATCH 67/72] testing --- .../arb-token-bridge-ui/src/hooks/useTransactionHistory.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 762962be26..642a3677ab 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -221,6 +221,8 @@ const useTransactionHistoryWithoutStatuses = ( ) => { const [isTestnetMode] = useIsTestnetMode() + console.log({ isTestnetMode }) + const cctpTransfersMainnet = useCctpFetching({ walletAddress: address, l1ChainId: ChainId.Ethereum, @@ -334,6 +336,8 @@ const useTransactionHistoryWithoutStatuses = ( [transactions] ) + console.log({ dedupedTransactions }) + return { data: dedupedTransactions, loading: depositsLoading || withdrawalsLoading || cctpLoading, @@ -625,6 +629,8 @@ export const useTransactionHistory = ( } } + console.log({ transactions }) + return { data: { transactions, From 43ca07ae351ef8d682bc4e14c5ce9543636cad21 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 21 Dec 2023 13:37:13 +0100 Subject: [PATCH 68/72] try --- packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts b/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts index 02b890ec75..8f4cc6121d 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts @@ -4,8 +4,7 @@ const testnetModeLocalStorageKey = 'arbitrum:bridge:settings:testnetMode' export const useIsTestnetMode = () => { const [isTestnetMode, setIsTestnetMode] = useLocalStorage( - testnetModeLocalStorageKey, - false + testnetModeLocalStorageKey ) return [isTestnetMode, setIsTestnetMode] as const From ffedd37ace3ed133220944c99ca36b75f2e19480 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 21 Dec 2023 13:42:40 +0100 Subject: [PATCH 69/72] try --- packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts b/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts index 8f4cc6121d..02b890ec75 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts @@ -4,7 +4,8 @@ const testnetModeLocalStorageKey = 'arbitrum:bridge:settings:testnetMode' export const useIsTestnetMode = () => { const [isTestnetMode, setIsTestnetMode] = useLocalStorage( - testnetModeLocalStorageKey + testnetModeLocalStorageKey, + false ) return [isTestnetMode, setIsTestnetMode] as const From 3875249d07ae7aeae8100a0a74dc0bd1378bdbd7 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 21 Dec 2023 13:55:11 +0100 Subject: [PATCH 70/72] fix --- .../src/hooks/useIsTestnetMode.ts | 3 +-- .../src/hooks/useTransactionHistory.ts | 14 ++++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts b/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts index 02b890ec75..8f4cc6121d 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts @@ -4,8 +4,7 @@ const testnetModeLocalStorageKey = 'arbitrum:bridge:settings:testnetMode' export const useIsTestnetMode = () => { const [isTestnetMode, setIsTestnetMode] = useLocalStorage( - testnetModeLocalStorageKey, - false + testnetModeLocalStorageKey ) return [isTestnetMode, setIsTestnetMode] as const diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 642a3677ab..89155cced3 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -221,8 +221,6 @@ const useTransactionHistoryWithoutStatuses = ( ) => { const [isTestnetMode] = useIsTestnetMode() - console.log({ isTestnetMode }) - const cctpTransfersMainnet = useCctpFetching({ walletAddress: address, l1ChainId: ChainId.Ethereum, @@ -293,7 +291,9 @@ const useTransactionHistoryWithoutStatuses = ( error: depositsError, isLoading: depositsLoading } = useSWRImmutable( - address ? ['tx_list', 'deposits', address, isTestnetMode] : null, + address && typeof isTestnetMode !== 'undefined' + ? ['tx_list', 'deposits', address, isTestnetMode] + : null, () => fetcher('deposits') ) @@ -302,7 +302,9 @@ const useTransactionHistoryWithoutStatuses = ( error: withdrawalsError, isLoading: withdrawalsLoading } = useSWRImmutable( - address ? ['tx_list', 'withdrawals', address, isTestnetMode] : null, + address && typeof isTestnetMode !== 'undefined' + ? ['tx_list', 'withdrawals', address, isTestnetMode] + : null, () => fetcher('withdrawals') ) @@ -336,8 +338,6 @@ const useTransactionHistoryWithoutStatuses = ( [transactions] ) - console.log({ dedupedTransactions }) - return { data: dedupedTransactions, loading: depositsLoading || withdrawalsLoading || cctpLoading, @@ -629,8 +629,6 @@ export const useTransactionHistory = ( } } - console.log({ transactions }) - return { data: { transactions, From b8903d21a1c02f057abeca4ad54e9e2ea4138d87 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 21 Dec 2023 14:13:49 +0100 Subject: [PATCH 71/72] revert --- .../arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts | 3 ++- .../src/hooks/useTransactionHistory.ts | 8 ++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts b/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts index 8f4cc6121d..02b890ec75 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useIsTestnetMode.ts @@ -4,7 +4,8 @@ const testnetModeLocalStorageKey = 'arbitrum:bridge:settings:testnetMode' export const useIsTestnetMode = () => { const [isTestnetMode, setIsTestnetMode] = useLocalStorage( - testnetModeLocalStorageKey + testnetModeLocalStorageKey, + false ) return [isTestnetMode, setIsTestnetMode] as const diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 89155cced3..762962be26 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -291,9 +291,7 @@ const useTransactionHistoryWithoutStatuses = ( error: depositsError, isLoading: depositsLoading } = useSWRImmutable( - address && typeof isTestnetMode !== 'undefined' - ? ['tx_list', 'deposits', address, isTestnetMode] - : null, + address ? ['tx_list', 'deposits', address, isTestnetMode] : null, () => fetcher('deposits') ) @@ -302,9 +300,7 @@ const useTransactionHistoryWithoutStatuses = ( error: withdrawalsError, isLoading: withdrawalsLoading } = useSWRImmutable( - address && typeof isTestnetMode !== 'undefined' - ? ['tx_list', 'withdrawals', address, isTestnetMode] - : null, + address ? ['tx_list', 'withdrawals', address, isTestnetMode] : null, () => fetcher('withdrawals') ) From 9acba66db91b3394288c81e32f4e83f056a570dc Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 21 Dec 2023 16:06:02 +0100 Subject: [PATCH 72/72] fix --- .../arb-token-bridge-ui/src/hooks/useTransactionHistory.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 762962be26..7f22011c90 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -305,7 +305,9 @@ const useTransactionHistoryWithoutStatuses = ( ) const deposits = [ - ...getDepositsWithoutStatusesFromCache(), + ...getDepositsWithoutStatusesFromCache().filter(tx => + isTestnetMode ? true : !isNetwork(tx.parentChainId).isTestnet + ), (depositsData || []).flat() ] @@ -319,6 +321,7 @@ const useTransactionHistoryWithoutStatuses = ( ].flat() // duplicates may occur when txs are taken from the local storage + // we don't use Set because it wouldn't dedupe objects with different reference (we fetch them from different sources) const dedupedTransactions = useMemo( () => Array.from(