From 92c55e7050ca6a80db523a2f50401cd2c92d5daf Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:31:44 +0000 Subject: [PATCH 01/49] feat: top nav bar --- .../public/icons/history.svg | 5 ++ .../public/icons/wallet.svg | 3 ++ .../components/MainContent/MainContent.tsx | 22 +++++--- .../src/components/TopNavBar.tsx | 51 +++++++++++++++++++ 4 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 packages/arb-token-bridge-ui/public/icons/history.svg create mode 100644 packages/arb-token-bridge-ui/public/icons/wallet.svg create mode 100644 packages/arb-token-bridge-ui/src/components/TopNavBar.tsx diff --git a/packages/arb-token-bridge-ui/public/icons/history.svg b/packages/arb-token-bridge-ui/public/icons/history.svg new file mode 100644 index 0000000000..0e766b0c49 --- /dev/null +++ b/packages/arb-token-bridge-ui/public/icons/history.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/arb-token-bridge-ui/public/icons/wallet.svg b/packages/arb-token-bridge-ui/public/icons/wallet.svg new file mode 100644 index 0000000000..6623ec3f47 --- /dev/null +++ b/packages/arb-token-bridge-ui/public/icons/wallet.svg @@ -0,0 +1,3 @@ + + + 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 158308db54..4265010e0f 100644 --- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx +++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx @@ -1,6 +1,7 @@ import { useEffect, useMemo } from 'react' import { useAccount } from 'wagmi' import { useLocalStorage } from '@uidotdev/usehooks' +import { Tab } from '@headlessui/react' import { TransferPanel } from '../TransferPanel/TransferPanel' import { SidePanel } from '../common/SidePanel' @@ -11,6 +12,7 @@ import { TransactionHistory } from '../TransactionHistory/TransactionHistory' import { useTransactionHistory } from '../../hooks/useTransactionHistory' import { isTxPending } from '../TransactionHistory/helpers' import { TransactionStatusInfo } from '../TransactionHistory/TransactionStatusInfo' +import { TopNavBar } from '../TopNavBar' function TransactionHistorySidePanel() { const { closeTransactionHistoryPanel } = useAppContextActions() @@ -55,14 +57,22 @@ export function MainContent() { return ( <> -
- - - +
+ + + {/* */} + + {/* BUY PANEL */} + + + + + + + +
- - {/* Settings panel */} diff --git a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx new file mode 100644 index 0000000000..ef1e46e84a --- /dev/null +++ b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx @@ -0,0 +1,51 @@ +import { Tab } from '@headlessui/react' +import { PaperAirplaneIcon } from '@heroicons/react/24/outline' +import { PropsWithChildren } from 'react' +import { twMerge } from 'tailwind-merge' +import Image from 'next/image' + +function StyledTab({ children, ...props }: PropsWithChildren) { + return ( + + {children} + + ) +} + +StyledTab.displayName = 'StyledTab' + +export function TopNavBar() { + return ( + + {/* + wallet icon + Buy + */} + + + Bridge + + + history icon + Txn History + + + ) +} From 9862a54db6ee937ac7237bf9b1cc3a2538bfb96d Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Mon, 11 Nov 2024 17:50:21 +0000 Subject: [PATCH 02/49] feat: move tx history --- .../components/MainContent/MainContent.tsx | 53 ++---------------- .../src/components/TopNavBar.tsx | 2 +- .../TransactionHistory/TransactionHistory.tsx | 54 ++++++++++++++----- .../TransactionStatusInfo.tsx | 19 ++----- packages/arb-token-bridge-ui/tsconfig.json | 27 +++++++--- packages/scripts/package.json | 2 +- yarn.lock | 11 ---- 7 files changed, 73 insertions(+), 95 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 4265010e0f..9399f7b735 100644 --- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx +++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx @@ -1,73 +1,28 @@ -import { useEffect, useMemo } from 'react' -import { useAccount } from 'wagmi' import { useLocalStorage } from '@uidotdev/usehooks' import { Tab } from '@headlessui/react' import { TransferPanel } from '../TransferPanel/TransferPanel' -import { SidePanel } from '../common/SidePanel' -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 { isTxPending } from '../TransactionHistory/helpers' -import { TransactionStatusInfo } from '../TransactionHistory/TransactionStatusInfo' import { TopNavBar } from '../TopNavBar' -function TransactionHistorySidePanel() { - const { closeTransactionHistoryPanel } = useAppContextActions() - const { - layout: { isTransactionHistoryPanelVisible } - } = useAppContextState() - const { address } = useAccount() - - const transactionHistoryProps = useTransactionHistory(address, { - runFetcher: true - }) - - const { transactions, updatePendingTransaction } = transactionHistoryProps - - const pendingTransactions = useMemo(() => { - return transactions.filter(isTxPending) - }, [transactions]) - - useEffect(() => { - const interval = setInterval(() => { - pendingTransactions.forEach(updatePendingTransaction) - }, 10_000) - - return () => clearInterval(interval) - }, [pendingTransactions, updatePendingTransaction]) - - return ( - - - - ) -} - export function MainContent() { const [isArbitrumStatsVisible] = useLocalStorage(statsLocalStorageKey) return ( <> -
+
- {/* */} {/* BUY PANEL */} - + - - + + diff --git a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx index ef1e46e84a..1b4d8f1297 100644 --- a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx +++ b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx @@ -21,7 +21,7 @@ export function TopNavBar() { return ( {/* 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 a51d4e9656..5f1b522e7a 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx @@ -1,10 +1,11 @@ import dayjs from 'dayjs' -import { useMemo } from 'react' +import { useEffect, useMemo } from 'react' import { Tab } from '@headlessui/react' import { create } from 'zustand' -import { UseTransactionHistoryResult } from '../../hooks/useTransactionHistory' +import { useTransactionHistory } from '../../hooks/useTransactionHistory' import { TransactionHistoryTable } from './TransactionHistoryTable' +import { TransactionStatusInfo } from '../TransactionHistory/TransactionStatusInfo' import { isTxClaimable, isTxCompleted, @@ -15,7 +16,7 @@ import { import { MergedTransaction } from '../../state/app/state' import { TabButton } from '../common/Tab' import { TransactionsTableDetails } from './TransactionsTableDetails' -import { Address } from '../../util/AddressUtils' +import { useAccount } from 'wagmi' const tabClasses = 'text-white px-3 mr-2 border-b-2 ui-selected:border-white ui-not-selected:border-transparent ui-not-selected:text-white/80 arb-hover' @@ -46,12 +47,35 @@ export const useTxDetailsStore = create(set => ({ reset: () => set({ tx: null }) })) -export const TransactionHistory = ({ - props -}: { - props: UseTransactionHistoryResult & { address: Address | undefined } -}) => { - const { transactions, address } = props +function useTransactionHistoryProps() { + const { address } = useAccount() + + const transactionHistoryProps = useTransactionHistory(address, { + runFetcher: true + }) + + const { transactions, updatePendingTransaction } = transactionHistoryProps + + const pendingTransactions = useMemo(() => { + return transactions.filter(isTxPending) + }, [transactions]) + + useEffect(() => { + const interval = setInterval(() => { + pendingTransactions.forEach(updatePendingTransaction) + }, 10_000) + + return () => clearInterval(interval) + }, [pendingTransactions, updatePendingTransaction]) + + return transactionHistoryProps +} + +export const TransactionHistory = () => { + const props = useTransactionHistoryProps() + const { transactions } = props + + const { address } = useAccount() const oldestTxTimeAgoString = useMemo(() => { return dayjs(transactions[transactions.length - 1]?.createdAt).toNow(true) @@ -94,8 +118,14 @@ export const TransactionHistory = ({ const settledTransactions = groupedTransactions.settled return ( - <> - +
+ + + - +
) } diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx index 88ab67d208..9d67a34a95 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx @@ -87,12 +87,7 @@ const Content = ({ ) } - return ( -
- - See transaction history -
- ) + return null } export const TransactionStatusInfo = () => { @@ -143,23 +138,17 @@ export const TransactionStatusInfo = () => { }, [numClaimableTransactions, numPendingTransactions, numRetryablesToRedeem]) return ( - +
) } diff --git a/packages/arb-token-bridge-ui/tsconfig.json b/packages/arb-token-bridge-ui/tsconfig.json index 6d8f505ddc..e569fc231f 100644 --- a/packages/arb-token-bridge-ui/tsconfig.json +++ b/packages/arb-token-bridge-ui/tsconfig.json @@ -5,17 +5,32 @@ "additional.d.ts", "next.config.js", "next-env.d.ts", - ".next/types/**/*.ts" + ".next/types/**/*.ts", + "build/types/**/*.ts" ], "compilerOptions": { "noEmit": true, "incremental": true, "jsx": "preserve", "paths": { - "@/images/*": ["./public/images/*"], - "@/icons/*": ["./public/icons/*"], - "@/token-bridge-sdk/*": ["./src/token-bridge-sdk/*"] - } + "@/images/*": [ + "./public/images/*" + ], + "@/icons/*": [ + "./public/icons/*" + ], + "@/token-bridge-sdk/*": [ + "./src/token-bridge-sdk/*" + ] + }, + "plugins": [ + { + "name": "next" + } + ], + "strictNullChecks": true }, - "exclude": ["node_modules"] + "exclude": [ + "node_modules" + ] } diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 13cb924a31..9e7591a891 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -17,7 +17,7 @@ "dependencies": { "@actions/core": "^1.10.1", "@actions/github": "^6.0.0", - "@arbitrum/sdk": "^4.0.1", + "@arbitrum/sdk": "^4.0.2", "@octokit/rest": "^21.0.2", "axios": "^1.7.7", "commander": "^12.1.0", diff --git a/yarn.lock b/yarn.lock index 6d37ba1c40..3e3e38038d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -78,17 +78,6 @@ tslib "^2.3.0" zen-observable-ts "^1.2.5" -"@arbitrum/sdk@^4.0.1": - version "4.0.1" - resolved "https://registry.npmjs.org/@arbitrum/sdk/-/sdk-4.0.1.tgz" - integrity sha512-uW0Pe/oICbmlHpIpYOaHHWsNQRG+3UbCa3s0SJsp2O1Kt9b0M0CX/fEdFOFLyAi3OxHonNEfzhfvQrALy9C3Yw== - dependencies: - "@ethersproject/address" "^5.0.8" - "@ethersproject/bignumber" "^5.1.1" - "@ethersproject/bytes" "^5.0.8" - async-mutex "^0.4.0" - ethers "^5.1.0" - "@arbitrum/sdk@^4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@arbitrum/sdk/-/sdk-4.0.2.tgz#23555858f49e2b237b94a65bd486c65edb7b1690" From b1384803b95bbcd0c8a12e0956cf835e1889fe3b Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 12 Nov 2024 17:28:38 +0000 Subject: [PATCH 03/49] dynamic height table --- .../src/components/App/AppContext.tsx | 26 +- .../components/MainContent/MainContent.tsx | 8 +- .../TransactionHistory/TransactionHistory.tsx | 8 +- .../TransactionHistoryTable.tsx | 231 +++++++++--------- .../TransactionStatusInfo.tsx | 5 +- .../common/HeaderAccountPopover.tsx | 15 +- .../src/hooks/useAccountMenu.ts | 9 - 7 files changed, 126 insertions(+), 176 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/App/AppContext.tsx b/packages/arb-token-bridge-ui/src/components/App/AppContext.tsx index e70d0713f0..8faa126b8e 100644 --- a/packages/arb-token-bridge-ui/src/components/App/AppContext.tsx +++ b/packages/arb-token-bridge-ui/src/components/App/AppContext.tsx @@ -9,15 +9,13 @@ type AppContextState = { layout: { isTransferPanelVisible: boolean isTransferring: boolean - isTransactionHistoryPanelVisible: boolean } } const initialState: AppContextState = { layout: { isTransferPanelVisible: true, - isTransferring: false, - isTransactionHistoryPanelVisible: false + isTransferring: false } } @@ -29,7 +27,6 @@ const AppContext = createContext([initialState, () => {}]) type Action = | { type: 'layout.set_is_transfer_panel_visible'; payload: boolean } | { type: 'layout.set_is_transferring'; payload: boolean } - | { type: 'layout.set_txhistory_panel_visible'; payload: boolean } function reducer(state: AppContextState, action: Action) { switch (action.type) { @@ -39,15 +36,6 @@ function reducer(state: AppContextState, action: Action) { layout: { ...state.layout, isTransferPanelVisible: action.payload } } - case 'layout.set_txhistory_panel_visible': - return { - ...state, - layout: { - ...state.layout, - isTransactionHistoryPanelVisible: action.payload - } - } - case 'layout.set_is_transferring': return { ...state, @@ -88,17 +76,7 @@ export const useAppContextActions = (dispatchOverride?: Dispatch) => { dispatch({ type: 'layout.set_is_transferring', payload }) } - const openTransactionHistoryPanel = () => { - dispatch({ type: 'layout.set_txhistory_panel_visible', payload: true }) - } - - const closeTransactionHistoryPanel = () => { - dispatch({ type: 'layout.set_txhistory_panel_visible', payload: false }) - } - return { - setTransferring, - openTransactionHistoryPanel, - closeTransactionHistoryPanel + setTransferring } } 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 9399f7b735..5447cd31ef 100644 --- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx +++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx @@ -13,15 +13,15 @@ export function MainContent() { return ( <> -
+
- + {/* BUY 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 5f1b522e7a..8e2e63b118 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx @@ -118,7 +118,7 @@ export const TransactionHistory = () => { const settledTransactions = groupedTransactions.settled return ( -
+
{ - - + + { oldestTxTimeAgoString={oldestTxTimeAgoString} /> - + ) => { +export const ContentWrapper = forwardRef< + HTMLDivElement, + PropsWithChildren<{ className?: string }> +>(({ children, className = '', ...props }, ref) => { return (
{children}
) -} +}) + +ContentWrapper.displayName = 'ContentWrapper' const TableHeader = ({ children, @@ -158,7 +158,19 @@ export const TransactionHistoryTable = ( const paused = !loading && !completed - const [tableHeight, setTableHeight] = useState(0) + const contentWrapperRef = useRef(null) + const tableRef = useRef(null) + + const tableHeight = useMemo(() => { + const SIDE_PANEL_HEADER_HEIGHT = 125 + const viewportHeight = window.innerHeight + const contentWrapperOffsetTop = contentWrapperRef.current?.offsetTop ?? 0 + return Math.max( + // we subtract a little padding at the end so that the table doesn't end at the edge of the screen + viewportHeight - contentWrapperOffsetTop - SIDE_PANEL_HEADER_HEIGHT, + 0 + ) + }, [contentWrapperRef.current?.offsetTop]) const pendingTokenDepositsCount = useMemo(() => { return transactions.filter(tx => isTokenDeposit(tx) && isTxPending(tx)) @@ -169,36 +181,15 @@ export const TransactionHistoryTable = ( return transactions.filter(isTxPending)[0]?.txId }, [transactions]) - // TODO: look into https://www.npmjs.com/package/react-intersection-observer that could simplify this + // recalculate table height when tx number changes, or when user selects different tab useEffect(() => { - // Calculate table height to be passed to the React Virtualized Table - const currentRef = contentAboveTable.current - const SIDE_PANEL_HEADER_HEIGHT = 125 - - // Adjust the table size whenever the content above it is resized - const observer = new ResizeObserver(entries => { - if (entries[0]) { - const aboveHeight = entries[0].contentRect.height - const viewportHeight = window.innerHeight - const newTableHeight = Math.max( - // we subtract a little padding at the end so that the table doesn't end at the edge of the screen - viewportHeight - aboveHeight - SIDE_PANEL_HEADER_HEIGHT - 20, - 0 - ) - setTableHeight(newTableHeight) - } - }) - - if (currentRef) { - observer.observe(currentRef) - } - - return () => { - if (currentRef) { - observer.unobserve(currentRef) - } - } - }, [transactions.length]) + tableRef.current?.recomputeRowHeights() + }, [ + transactions.length, + selectedTabIndex, + isTxHistoryEmpty, + contentWrapperRef.current?.offsetTop + ]) if (isTxHistoryEmpty) { return ( @@ -213,10 +204,13 @@ export const TransactionHistoryTable = ( } return ( - +
{pendingTokenDepositsCount > 0 && }
- - {() => ( -
( -
- {props.columns} -
- )} - className="table-auto last:border-b-0" - rowGetter={({ index }) => transactions[index]} - rowRenderer={({ index, style }) => { - const tx = transactions[index] +
( +
+ {props.columns} +
+ )} + className="table-auto last:border-b-0" + rowGetter={({ index }) => transactions[index]} + rowRenderer={({ index, style }) => { + const tx = transactions[index] - if (!tx) { - return null - } + if (!tx) { + return null + } - const isLastRow = index + 1 === transactions.length - const key = `${tx.parentChainId}-${tx.childChainId}-${tx.txId}` - const secondsPassed = dayjs().diff(dayjs(tx.createdAt), 'second') + const isLastRow = index + 1 === transactions.length + const key = `${tx.parentChainId}-${tx.childChainId}-${tx.txId}` + const secondsPassed = dayjs().diff(dayjs(tx.createdAt), 'second') - // only blink the topmost tx, in case many txs are queued in a short amount of time - const isTopmostPendingTx = - topmostPendingTxId && topmostPendingTxId === tx.txId + // only blink the topmost tx, in case many txs are queued in a short amount of time + const isTopmostPendingTx = + topmostPendingTxId && topmostPendingTxId === tx.txId - return ( -
- -
- ) - }} - > - TIME} - /> - TOKEN} - /> - FROM} - /> - TO} - /> - STATUS} - /> -
- )} - + return ( +
+ +
+ ) + }} + > + TIME} + /> + TOKEN} + /> + FROM} + /> + TO} + /> + STATUS} + /> + ) } diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx index 9d67a34a95..11d62a41c1 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx @@ -92,7 +92,6 @@ const Content = ({ export const TransactionStatusInfo = () => { const { address } = useAccount() - const { openTransactionHistoryPanel } = useAppContextActions() const { transactions } = useTransactionHistory(address) const { @@ -137,6 +136,10 @@ export const TransactionStatusInfo = () => { return 'bg-gray-1 text-white/70' }, [numClaimableTransactions, numPendingTransactions, numRetryablesToRedeem]) + if (Content === null) { + return null + } + return (
- {/* Transactions button */} - {isCorrectNetworkConnected && ( - - )} - {/* Explorer button */} {isCorrectNetworkConnected && chain && ( { }) const { chain } = useNetwork() - const { openTransactionHistoryPanel } = useAppContextActions() const [, setQueryParams] = useArbQueryParams() const [udInfo, setUDInfo] = useState(udInfoDefaults) @@ -101,17 +98,11 @@ export const useAccountMenu = () => { resolveUdName() }, [address, l1Provider]) - function openTransactionHistory() { - openTransactionHistoryPanel() - trackEvent('Open Transaction History Click', { pageElement: 'Header' }) - } - return { address, accountShort, ensName, ensAvatar, - openTransactionHistory, disconnect, udInfo, chain, From d712e970e71f47471594b005d2c53b9e26878b5b Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Wed, 13 Nov 2024 13:34:59 +0000 Subject: [PATCH 04/49] update mobile view --- .../components/MainContent/MainContent.tsx | 2 +- .../TransactionHistory/TransactionHistory.tsx | 12 +++++----- .../TransactionHistoryTable.tsx | 22 ++++++++++--------- .../TransactionStatusInfo.tsx | 9 ++------ .../TransactionsTableRow.tsx | 2 +- 5 files changed, 23 insertions(+), 24 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 5447cd31ef..8143f6615d 100644 --- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx +++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx @@ -21,7 +21,7 @@ export function MainContent() { - + 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 8e2e63b118..3d270b8e1f 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx @@ -118,26 +118,28 @@ export const TransactionHistory = () => { const settledTransactions = groupedTransactions.settled return ( -
- +
+
+ +
- Pending transactions + Pending transactions - Settled transactions + Settled transactions 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 4605af3dda..830fa9f36d 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx @@ -151,8 +151,8 @@ export const TransactionHistoryTable = ( oldestTxTimeAgoString } = props - const contentAboveTable = useRef(null) - + const TABLE_HEADER_HEIGHT = 52 + const TABLE_ROW_HEIGHT = 60 const isTxHistoryEmpty = transactions.length === 0 const isPendingTab = selectedTabIndex === 0 @@ -162,6 +162,9 @@ export const TransactionHistoryTable = ( const tableRef = useRef(null) const tableHeight = useMemo(() => { + if (window.innerWidth < 768) { + return TABLE_ROW_HEIGHT * (transactions.length + 1) + TABLE_HEADER_HEIGHT + } const SIDE_PANEL_HEADER_HEIGHT = 125 const viewportHeight = window.innerHeight const contentWrapperOffsetTop = contentWrapperRef.current?.offsetTop ?? 0 @@ -170,7 +173,7 @@ export const TransactionHistoryTable = ( viewportHeight - contentWrapperOffsetTop - SIDE_PANEL_HEADER_HEIGHT, 0 ) - }, [contentWrapperRef.current?.offsetTop]) + }, [contentWrapperRef.current?.offsetTop, transactions.length]) const pendingTokenDepositsCount = useMemo(() => { return transactions.filter(tx => isTokenDeposit(tx) && isTxPending(tx)) @@ -206,14 +209,13 @@ export const TransactionHistoryTable = ( return (
{loading ? (
@@ -221,7 +223,7 @@ export const TransactionHistoryTable = (
) : ( -
+
@@ -241,11 +243,11 @@ export const TransactionHistoryTable = ( ref={tableRef} width={960} height={tableHeight} - rowHeight={60} + rowHeight={TABLE_ROW_HEIGHT} rowCount={transactions.length} - headerHeight={52} + headerHeight={TABLE_HEADER_HEIGHT} headerRowRenderer={props => ( -
+
{props.columns}
)} diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx index 11d62a41c1..e284e27e8e 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx @@ -5,18 +5,13 @@ import { useAccount } from 'wagmi' import { useMemo } from 'react' -import { - DocumentTextIcon, - ExclamationTriangleIcon -} from '@heroicons/react/24/outline' +import { ExclamationTriangleIcon } from '@heroicons/react/24/outline' import { twMerge } from 'tailwind-merge' import Image from 'next/image' import ArrowsIcon from '@/images/arrows.svg' import { isDepositReadyToRedeem } from '../../state/app/utils' -import { useAppContextActions } from '../App/AppContext' import { useTransactionHistory } from '../../hooks/useTransactionHistory' -import { Button } from '../common/Button' import { isTxClaimable, isTxPending } from './helpers' const Content = ({ @@ -143,7 +138,7 @@ export const TransactionStatusInfo = () => { return (
diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRow.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRow.tsx index 515ba807c6..ea4a54db0f 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRow.tsx @@ -189,7 +189,7 @@ export function TransactionsTableRow({
From 15b4838f8d5569d7c65fae6168bae868e3b67b8e Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:16:34 +0000 Subject: [PATCH 05/49] fix deleted imports --- .../src/components/Sidebar/AccountMenuItem.tsx | 7 ------- .../components/TransferPanel/TransferPanel.tsx | 5 +---- packages/arb-token-bridge-ui/tsconfig.json | 16 ++++------------ 3 files changed, 5 insertions(+), 23 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/Sidebar/AccountMenuItem.tsx b/packages/arb-token-bridge-ui/src/components/Sidebar/AccountMenuItem.tsx index 96d648990e..07e8cc4f7b 100644 --- a/packages/arb-token-bridge-ui/src/components/Sidebar/AccountMenuItem.tsx +++ b/packages/arb-token-bridge-ui/src/components/Sidebar/AccountMenuItem.tsx @@ -17,7 +17,6 @@ export const AccountMenuItem = () => { accountShort, ensName, ensAvatar, - openTransactionHistory, disconnect, udInfo, chain, @@ -36,12 +35,6 @@ export const AccountMenuItem = () => { /> } > - } - onClick={openTransactionHistory} - isMobile - /> {chain && ( Date: Wed, 13 Nov 2024 16:34:44 +0000 Subject: [PATCH 06/49] hide status info box when loading --- .../components/TransactionHistory/TransactionStatusInfo.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx index e284e27e8e..3d4fbd054d 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx @@ -131,7 +131,11 @@ export const TransactionStatusInfo = () => { return 'bg-gray-1 text-white/70' }, [numClaimableTransactions, numPendingTransactions, numRetryablesToRedeem]) - if (Content === null) { + if ( + numClaimableTransactions === 0 && + numRetryablesToRedeem === 0 && + numPendingTransactions === 0 + ) { return null } From d15fd5eeb41dd7d113db3cf4de0b9ba7a38fcc55 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:40:07 +0000 Subject: [PATCH 07/49] add transaction status dot to tab --- .../src/components/TopNavBar.tsx | 7 ++- .../TransactionStatusInfo.tsx | 52 ++-------------- .../useTransactionReminderInfo.ts | 60 +++++++++++++++++++ .../arb-token-bridge-ui/tailwind.config.js | 5 ++ 4 files changed, 76 insertions(+), 48 deletions(-) create mode 100644 packages/arb-token-bridge-ui/src/components/TransactionHistory/useTransactionReminderInfo.ts diff --git a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx index 1b4d8f1297..1479724ea6 100644 --- a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx +++ b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx @@ -3,6 +3,7 @@ import { PaperAirplaneIcon } from '@heroicons/react/24/outline' import { PropsWithChildren } from 'react' import { twMerge } from 'tailwind-merge' import Image from 'next/image' +import { useTransactionReminderInfo } from './TransactionHistory/useTransactionReminderInfo' function StyledTab({ children, ...props }: PropsWithChildren) { return ( @@ -18,6 +19,7 @@ function StyledTab({ children, ...props }: PropsWithChildren) { StyledTab.displayName = 'StyledTab' export function TopNavBar() { + const { colorClassName } = useTransactionReminderInfo() return ( - Txn History + Txn History{' '} + ) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx index 3d4fbd054d..fb4ee1f9d7 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionStatusInfo.tsx @@ -3,16 +3,12 @@ Format: "You have [X] deposits to retry and [Y] withdrawals ready to claim. [CTA]" */ -import { useAccount } from 'wagmi' -import { useMemo } from 'react' import { ExclamationTriangleIcon } from '@heroicons/react/24/outline' import { twMerge } from 'tailwind-merge' import Image from 'next/image' import ArrowsIcon from '@/images/arrows.svg' -import { isDepositReadyToRedeem } from '../../state/app/utils' -import { useTransactionHistory } from '../../hooks/useTransactionHistory' -import { isTxClaimable, isTxPending } from './helpers' +import { useTransactionReminderInfo } from './useTransactionReminderInfo' const Content = ({ numClaimableTransactions, @@ -86,50 +82,12 @@ const Content = ({ } export const TransactionStatusInfo = () => { - const { address } = useAccount() - const { transactions } = useTransactionHistory(address) - const { numClaimableTransactions, numRetryablesToRedeem, - numPendingTransactions - } = useMemo(() => { - return transactions.reduce( - (acc, tx) => { - // standard bridge withdrawal - if (isTxClaimable(tx)) { - acc.numClaimableTransactions += 1 - } - // failed retryable - if (isDepositReadyToRedeem(tx)) { - acc.numRetryablesToRedeem += 1 - } - // all pending - if (isTxPending(tx)) { - acc.numPendingTransactions += 1 - } - return acc - }, - { - numClaimableTransactions: 0, - numRetryablesToRedeem: 0, - numPendingTransactions: 0 - } - ) - }, [transactions]) - - const buttonClassName = useMemo(() => { - if (numRetryablesToRedeem > 0) { - return 'bg-red-700' - } - if (numClaimableTransactions > 0) { - return 'bg-lime-dark' - } - if (numPendingTransactions > 0) { - return 'bg-cyan-dark' - } - return 'bg-gray-1 text-white/70' - }, [numClaimableTransactions, numPendingTransactions, numRetryablesToRedeem]) + numPendingTransactions, + colorClassName + } = useTransactionReminderInfo() if ( numClaimableTransactions === 0 && @@ -143,7 +101,7 @@ export const TransactionStatusInfo = () => {
{ + return transactions.reduce( + (acc, tx) => { + // standard bridge withdrawal + if (isTxClaimable(tx)) { + acc.numClaimableTransactions += 1 + } + // failed retryable + if (isDepositReadyToRedeem(tx)) { + acc.numRetryablesToRedeem += 1 + } + // all pending + if (isTxPending(tx)) { + acc.numPendingTransactions += 1 + } + return acc + }, + { + numClaimableTransactions: 0, + numRetryablesToRedeem: 0, + numPendingTransactions: 0 + } + ) + }, [transactions]) + + const colorClassName = useMemo(() => { + if (numRetryablesToRedeem > 0) { + return { dark: 'bg-red-700', light: 'bg-retry' } + } + if (numClaimableTransactions > 0) { + return { dark: 'bg-lime-dark', light: 'bg-claim' } + } + if (numPendingTransactions > 0) { + return { dark: 'bg-cyan-dark', light: 'bg-pending' } + } + return { dark: 'bg-gray-1 text-white/70', light: '' } + }, [numClaimableTransactions, numPendingTransactions, numRetryablesToRedeem]) + + return { + numClaimableTransactions, + numRetryablesToRedeem, + numPendingTransactions, + colorClassName + } +} diff --git a/packages/arb-token-bridge-ui/tailwind.config.js b/packages/arb-token-bridge-ui/tailwind.config.js index 687d267831..355dd9a77c 100644 --- a/packages/arb-token-bridge-ui/tailwind.config.js +++ b/packages/arb-token-bridge-ui/tailwind.config.js @@ -32,6 +32,11 @@ module.exports = { 'orange-dark': '#60461F', 'lime-dark': '#31572A', + // TRANSACTION STATUS COLORS + claim: '#6AD28A', + retry: '#CD0000', + pending: '#CCB069', + // NEUTRAL (GRAYS) 'gray-1': '#191919', 'gray-2': '#E5E5E5', From 8f122031e89f98d273485dab18214af4de3a397a Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:10:30 +0000 Subject: [PATCH 08/49] move hook for reuse --- .../src/components/TopNavBar.tsx | 4 +++ .../TransactionHistory/TransactionHistory.tsx | 35 +++---------------- .../useTransactionHistoryUpdater.ts | 29 +++++++++++++++ 3 files changed, 38 insertions(+), 30 deletions(-) create mode 100644 packages/arb-token-bridge-ui/src/components/TransactionHistory/useTransactionHistoryUpdater.ts diff --git a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx index 1479724ea6..a0a4a66abe 100644 --- a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx +++ b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx @@ -4,6 +4,7 @@ import { PropsWithChildren } from 'react' import { twMerge } from 'tailwind-merge' import Image from 'next/image' import { useTransactionReminderInfo } from './TransactionHistory/useTransactionReminderInfo' +import { useTransactionHistoryUpdater } from './TransactionHistory/useTransactionHistoryUpdater' function StyledTab({ children, ...props }: PropsWithChildren) { return ( @@ -20,6 +21,9 @@ StyledTab.displayName = 'StyledTab' export function TopNavBar() { const { colorClassName } = useTransactionReminderInfo() + + useTransactionHistoryUpdater() + return ( (set => ({ reset: () => set({ tx: null }) })) -function useTransactionHistoryProps() { - const { address } = useAccount() - - const transactionHistoryProps = useTransactionHistory(address, { - runFetcher: true - }) - - const { transactions, updatePendingTransaction } = transactionHistoryProps - - const pendingTransactions = useMemo(() => { - return transactions.filter(isTxPending) - }, [transactions]) - - useEffect(() => { - const interval = setInterval(() => { - pendingTransactions.forEach(updatePendingTransaction) - }, 10_000) - - return () => clearInterval(interval) - }, [pendingTransactions, updatePendingTransaction]) - - return transactionHistoryProps -} - export const TransactionHistory = () => { - const props = useTransactionHistoryProps() - const { transactions } = props - const { address } = useAccount() + const props = useTransactionHistoryUpdater() + const { transactions } = props const oldestTxTimeAgoString = useMemo(() => { return dayjs(transactions[transactions.length - 1]?.createdAt).toNow(true) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/useTransactionHistoryUpdater.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/useTransactionHistoryUpdater.ts new file mode 100644 index 0000000000..119b150dd4 --- /dev/null +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/useTransactionHistoryUpdater.ts @@ -0,0 +1,29 @@ +import { useAccount } from 'wagmi' +import { useTransactionHistory } from '../../hooks/useTransactionHistory' +import { useEffect, useMemo } from 'react' + +import { isTxPending } from './helpers' + +export function useTransactionHistoryUpdater() { + const { address } = useAccount() + + const transactionHistoryProps = useTransactionHistory(address, { + runFetcher: true + }) + + const { transactions, updatePendingTransaction } = transactionHistoryProps + + const pendingTransactions = useMemo(() => { + return transactions.filter(isTxPending) + }, [transactions]) + + useEffect(() => { + const interval = setInterval(() => { + pendingTransactions.forEach(updatePendingTransaction) + }, 10_000) + + return () => clearInterval(interval) + }, [pendingTransactions, updatePendingTransaction]) + + return transactionHistoryProps +} From 2ae703e2ba704ed6231e28423a987cfdb7243556 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:42:09 +0000 Subject: [PATCH 09/49] update --- .../src/components/MainContent/MainContent.tsx | 4 +++- .../arb-token-bridge-ui/src/components/Sidebar/AppSidebar.tsx | 2 +- packages/arb-token-bridge-ui/src/components/TopNavBar.tsx | 2 ++ packages/arb-token-bridge-ui/src/components/common/Layout.tsx | 2 +- 4 files changed, 7 insertions(+), 3 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 8143f6615d..5b504ed550 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 { useLocalStorage } from '@uidotdev/usehooks' import { Tab } from '@headlessui/react' +import { useState } from 'react' import { TransferPanel } from '../TransferPanel/TransferPanel' import { ArbitrumStats, statsLocalStorageKey } from './ArbitrumStats' @@ -10,11 +11,12 @@ import { TopNavBar } from '../TopNavBar' export function MainContent() { const [isArbitrumStatsVisible] = useLocalStorage(statsLocalStorageKey) + const [selectedIndex, setSelectedIndex] = useState(0) return ( <>
- + {/* BUY PANEL */} diff --git a/packages/arb-token-bridge-ui/src/components/Sidebar/AppSidebar.tsx b/packages/arb-token-bridge-ui/src/components/Sidebar/AppSidebar.tsx index 191048e667..c6cb166b3f 100644 --- a/packages/arb-token-bridge-ui/src/components/Sidebar/AppSidebar.tsx +++ b/packages/arb-token-bridge-ui/src/components/Sidebar/AppSidebar.tsx @@ -11,7 +11,7 @@ const DynamicSidebar = dynamic( export const AppSidebar = () => { const posthog = usePostHog() return ( -
+
) diff --git a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx index a0a4a66abe..b9749a5105 100644 --- a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx +++ b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx @@ -22,6 +22,8 @@ StyledTab.displayName = 'StyledTab' export function TopNavBar() { const { colorClassName } = useTransactionReminderInfo() + // without calling this, the site won't fetch the transactions and + // hence we won't be able to determine the transaction status (the dot) useTransactionHistoryUpdater() return ( diff --git a/packages/arb-token-bridge-ui/src/components/common/Layout.tsx b/packages/arb-token-bridge-ui/src/components/common/Layout.tsx index 119e218f30..b88df815e4 100644 --- a/packages/arb-token-bridge-ui/src/components/common/Layout.tsx +++ b/packages/arb-token-bridge-ui/src/components/common/Layout.tsx @@ -52,7 +52,7 @@ export function Layout(props: LayoutProps) { aria-hidden />
-
+
From 7bf8023652e4415595d06d6f6a9b524cb9c6ae1e Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:27:48 +0000 Subject: [PATCH 10/49] auto open tx history tab after tx --- .../components/MainContent/MainContent.tsx | 21 +++++++++++++++++-- .../src/components/TopNavBar.tsx | 5 ----- .../TransferPanel/TransferPanel.tsx | 4 ++++ 3 files changed, 23 insertions(+), 7 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 5b504ed550..f0f39b74d4 100644 --- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx +++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx @@ -1,17 +1,34 @@ import { useLocalStorage } from '@uidotdev/usehooks' import { Tab } from '@headlessui/react' -import { useState } from 'react' +import { create } from 'zustand' import { TransferPanel } from '../TransferPanel/TransferPanel' import { ArbitrumStats, statsLocalStorageKey } from './ArbitrumStats' import { SettingsDialog } from '../common/SettingsDialog' import { TransactionHistory } from '../TransactionHistory/TransactionHistory' import { TopNavBar } from '../TopNavBar' +import { useTransactionHistoryUpdater } from '../TransactionHistory/useTransactionHistoryUpdater' + +type SelectedTabIndexStore = { + selectedIndex: number + setSelectedIndex: (index: number) => void + openTransactionHistory: () => void +} + +export const useSelectedTabIndex = create(set => ({ + selectedIndex: 0, + setSelectedIndex: (index: number) => set({ selectedIndex: index }), + openTransactionHistory: () => set({ selectedIndex: 1 }) +})) export function MainContent() { const [isArbitrumStatsVisible] = useLocalStorage(statsLocalStorageKey) - const [selectedIndex, setSelectedIndex] = useState(0) + const { selectedIndex, setSelectedIndex } = useSelectedTabIndex() + + // without calling this, the site won't fetch the transactions and + // hence we won't be able to determine the transaction status (the dot) + useTransactionHistoryUpdater() return ( <> diff --git a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx index b9749a5105..911c9b5422 100644 --- a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx +++ b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx @@ -4,7 +4,6 @@ import { PropsWithChildren } from 'react' import { twMerge } from 'tailwind-merge' import Image from 'next/image' import { useTransactionReminderInfo } from './TransactionHistory/useTransactionReminderInfo' -import { useTransactionHistoryUpdater } from './TransactionHistory/useTransactionHistoryUpdater' function StyledTab({ children, ...props }: PropsWithChildren) { return ( @@ -22,10 +21,6 @@ StyledTab.displayName = 'StyledTab' export function TopNavBar() { const { colorClassName } = useTransactionReminderInfo() - // without calling this, the site won't fetch the transactions and - // hence we won't be able to determine the transaction status (the dot) - useTransactionHistoryUpdater() - return ( Date: Mon, 18 Nov 2024 15:29:44 +0000 Subject: [PATCH 11/49] update e2e --- .../arb-token-bridge-ui/src/components/TopNavBar.tsx | 4 ++-- .../tests/e2e/specs/batchDeposit.cy.ts | 4 ++-- .../tests/e2e/specs/depositCctp.cy.ts | 1 - .../tests/e2e/specs/depositERC20.cy.ts | 4 ++-- .../tests/e2e/specs/depositNativeToken.cy.ts | 4 ++-- .../tests/e2e/specs/redeemRetryable.cy.ts | 2 +- .../tests/e2e/specs/withdrawCctp.cy.ts | 1 - .../tests/e2e/specs/withdrawERC20.cy.ts | 6 +++--- .../tests/e2e/specs/withdrawNativeToken.cy.ts | 6 +++--- .../arb-token-bridge-ui/tests/support/commands.ts | 11 ++++++++--- 10 files changed, 23 insertions(+), 20 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx index 911c9b5422..253a4ce5ed 100644 --- a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx +++ b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx @@ -36,11 +36,11 @@ export function TopNavBar() { /> Buy */} - + Bridge - + { duration: 'a few seconds ago', ...txData }) - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() }) context('funds should reach destination account successfully', () => { @@ -268,7 +268,7 @@ describe('Batch Deposit', () => { Cypress.env('CUSTOM_DESTINATION_ADDRESS') ) cy.closeTransactionDetails() - cy.closeTransactionHistoryPanel() + cy.cy.switchToTransferPanelTab() }) context('transfer panel amount should be reset', () => { diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/depositCctp.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/depositCctp.cy.ts index f655a787ce..40b26f6fd6 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/depositCctp.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/depositCctp.cy.ts @@ -113,7 +113,6 @@ describe('Deposit USDC through CCTP', () => { it('should claim deposit', () => { cy.claimCctp(0.00014, { accept: false }) - cy.closeTransactionHistoryPanel() cy.claimCctp(0.00015, { accept: false }) }) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts index 1cd9ac5c13..c7d3f00c57 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/depositERC20.cy.ts @@ -99,7 +99,7 @@ describe('Deposit Token', () => { }) context('transfer panel amount should be reset', () => { - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() cy.findAmountInput().should('have.value', '') cy.findMoveFundsButton().should('be.disabled') }) @@ -184,7 +184,7 @@ describe('Deposit Token', () => { context('funds should reach destination account successfully', () => { // close transaction history - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() // the custom destination address should now have some balance greater than zero cy.findByLabelText(`${testCase.symbol} balance amount on childChain`) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/depositNativeToken.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/depositNativeToken.cy.ts index cab1fb0e21..a07b4fe20e 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/depositNativeToken.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/depositNativeToken.cy.ts @@ -38,7 +38,7 @@ describe('Deposit native token', () => { amount: ETHAmountToDeposit, symbol: nativeTokenSymbol }) - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() cy.findAmountInput().should('have.value', '') cy.findMoveFundsButton().should('be.disabled') }) @@ -77,7 +77,7 @@ describe('Deposit native token', () => { ) cy.closeTransactionDetails() - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() cy.findAmountInput().should('have.value', '') cy.findMoveFundsButton().should('be.disabled') }) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts index 4aea7f5971..304d29f908 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts @@ -94,7 +94,7 @@ describe('Redeem ERC20 Deposit', () => { symbol: 'WETH' }) - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() // wait for the destination balance to update cy.wait(5_000) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts index e1e062f1c4..7c38d25a8b 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawCctp.cy.ts @@ -97,7 +97,6 @@ describe('Withdraw USDC through CCTP', () => { it('should claim deposit', () => { cy.changeMetamaskNetwork('sepolia') cy.claimCctp(0.00012, { accept: true }) - cy.closeTransactionHistoryPanel() cy.claimCctp(0.00013, { accept: true }) }) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts index bbc0ff696f..8165b622a3 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts @@ -142,7 +142,7 @@ describe('Withdraw ERC20 Token', () => { }) context('transfer panel amount should be reset', () => { - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() cy.findAmountInput().should('have.value', '') cy.findMoveFundsButton().should('be.disabled') }) @@ -175,7 +175,7 @@ describe('Withdraw ERC20 Token', () => { })}` ).should('be.visible') - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() cy.searchAndSelectToken({ tokenName: testCase.symbol, @@ -265,7 +265,7 @@ describe('Withdraw ERC20 Token', () => { // close popup cy.closeTransactionDetails() - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() // the balance on the source chain should not be the same as before cy.findByLabelText(`${testCase.symbol} balance amount on childChain`) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawNativeToken.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawNativeToken.cy.ts index 6bb2fd751d..ac091e2966 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawNativeToken.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawNativeToken.cy.ts @@ -96,7 +96,7 @@ describe('Withdraw native token', () => { }) context('transfer panel amount should be reset', () => { - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() cy.findAmountInput().should('have.value', '') cy.findMoveFundsButton().should('be.disabled') }) @@ -128,7 +128,7 @@ describe('Withdraw native token', () => { })}` ).should('be.visible') - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() // the balance on the destination chain should not be the same as before cy.findByLabelText( @@ -194,7 +194,7 @@ describe('Withdraw native token', () => { cy.closeTransactionDetails() context('transfer panel amount should be reset', () => { - cy.closeTransactionHistoryPanel() + cy.switchToTransferPanelTab() cy.findAmountInput().should('have.value', '') cy.findMoveFundsButton().should('be.disabled') }) diff --git a/packages/arb-token-bridge-ui/tests/support/commands.ts b/packages/arb-token-bridge-ui/tests/support/commands.ts index fef1e5d1f0..bf1bd5cb65 100644 --- a/packages/arb-token-bridge-ui/tests/support/commands.ts +++ b/packages/arb-token-bridge-ui/tests/support/commands.ts @@ -256,8 +256,12 @@ export function findSelectTokenButton( .should('have.text', text) } -export function closeTransactionHistoryPanel() { - cy.findByLabelText('Close side panel').click() +export function switchToTransferPanelTab() { + return cy.findByLabelText('Switch to Bridge Tab').click() +} + +export function switchToTransferHistoryTab() { + return cy.findByLabelText('Switch to Transaction History Tab').click() } export function openTransactionDetails({ @@ -398,7 +402,8 @@ Cypress.Commands.addAll({ findGasFeeSummary, findMoveFundsButton, findSelectTokenButton, - closeTransactionHistoryPanel, + switchToTransferPanelTab, + switchToTransferHistoryTab, openTransactionDetails, closeTransactionDetails, findTransactionInTransactionHistory, From 5707d7dec4a4d7dea93cef46b40f70fafa60eff4 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Mon, 18 Nov 2024 17:32:58 +0000 Subject: [PATCH 12/49] update e2e --- .../tests/e2e/cypress.d.ts | 8 +-- .../tests/e2e/specs/batchDeposit.cy.ts | 2 +- .../tests/e2e/specs/readClassicDeposits.cy.ts | 6 +-- .../tests/e2e/specs/redeemRetryable.cy.ts | 2 +- .../tests/e2e/specs/txHistory.cy.ts | 4 +- .../tests/support/commands.ts | 51 ++++++++----------- 6 files changed, 33 insertions(+), 40 deletions(-) diff --git a/packages/arb-token-bridge-ui/tests/e2e/cypress.d.ts b/packages/arb-token-bridge-ui/tests/e2e/cypress.d.ts index 5b44d31925..77eaaa2609 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/cypress.d.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/cypress.d.ts @@ -24,8 +24,9 @@ import { findClaimButton, selectTransactionsPanelTab, confirmSpending, - closeTransactionHistoryPanel, - claimCctp + claimCctp, + switchToTransferPanelTab, + switchToTransactionHistoryTab } from '../support/commands' import { NetworkType, NetworkName } from '../support/common' @@ -67,7 +68,8 @@ declare global { findSelectTokenButton: typeof findSelectTokenButton openTransactionDetails: typeof openTransactionDetails closeTransactionDetails: typeof closeTransactionDetails - closeTransactionHistoryPanel: typeof closeTransactionHistoryPanel + switchToTransferPanelTab: typeof switchToTransferPanelTab + switchToTransactionHistoryTab: typeof switchToTransactionHistoryTab findTransactionDetailsCustomDestinationAddress: typeof findTransactionDetailsCustomDestinationAddress findTransactionInTransactionHistory: typeof findTransactionInTransactionHistory findClaimButton: typeof findClaimButton diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/batchDeposit.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/batchDeposit.cy.ts index 950d613f07..b801414f28 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/batchDeposit.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/batchDeposit.cy.ts @@ -268,7 +268,7 @@ describe('Batch Deposit', () => { Cypress.env('CUSTOM_DESTINATION_ADDRESS') ) cy.closeTransactionDetails() - cy.cy.switchToTransferPanelTab() + cy.switchToTransferPanelTab() }) context('transfer panel amount should be reset', () => { diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts index 727778240c..c88e3caaa7 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts @@ -29,7 +29,7 @@ function mockClassicDepositTransaction( l2NetworkID: '42161', timestampCreated: dateYearAgo.toISOString(), timestampResolved: dateYearAgo.toISOString(), - l1ToL2MsgData: { + parentToChildMsgData: { fetchingUpdate: false, status: ParentToChildMessageStatus.NOT_YET_CREATED, retryableCreationTxID: undefined, @@ -61,7 +61,7 @@ describe('Read classic deposit messages', () => { ]) ) - cy.openTransactionsPanel('settled') + cy.switchToTransactionHistoryTab('settled') const destinationTxHash = '0xd3ff2a70a115411e1ae4917351dca49281368684394d0dcac136fa08d9d9b436' @@ -92,7 +92,7 @@ describe('Read classic deposit messages', () => { ]) ) - cy.openTransactionsPanel('settled') + cy.switchToTransactionHistoryTab('settled') const destinationTxHash = '0x6cecd3bfc3ec73181c4ac0253d3f51e5aa8d26157ca7439ff9ab465de14a436f' diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts index 304d29f908..1c672f17f1 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts @@ -69,7 +69,7 @@ describe('Redeem ERC20 Deposit', () => { context('deposit should be redeemed', () => { // open transaction history and wait for deposit to fetch data - cy.openTransactionsPanel('pending') + cy.switchToTransactionHistoryTab('pending') // find the Retry button and the amount in the row cy.findTransactionInTransactionHistory({ diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/txHistory.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/txHistory.cy.ts index c9f1c0c53a..e1608e2962 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/txHistory.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/txHistory.cy.ts @@ -13,7 +13,7 @@ describe('Transaction History', () => { }) // open tx history panel context('open transactions history panel', () => { - cy.openTransactionsPanel('pending') + cy.switchToTransactionHistoryTab('pending') cy.findAllByTestId(CLAIMABLE_ROW_IDENTIFIER) .its('length') .should('be.gt', 0) @@ -30,7 +30,7 @@ describe('Transaction History', () => { } }) context('open transactions history panel', () => { - cy.openTransactionsPanel('settled') + cy.switchToTransactionHistoryTab('settled') cy.findAllByTestId(CLAIMABLE_ROW_IDENTIFIER) .its('length') .should('be.gt', 0) diff --git a/packages/arb-token-bridge-ui/tests/support/commands.ts b/packages/arb-token-bridge-ui/tests/support/commands.ts index bf1bd5cb65..0daf8af408 100644 --- a/packages/arb-token-bridge-ui/tests/support/commands.ts +++ b/packages/arb-token-bridge-ui/tests/support/commands.ts @@ -108,31 +108,6 @@ export const selectTransactionsPanelTab = (tab: 'pending' | 'settled') => { .and('equal', 'selected') } -export const openTransactionsPanel = (tab: 'pending' | 'settled') => { - cy.log(`opening transactions panel on ${tab}`) - cy.findByRole('button', { name: /account header button/i }) - .should('be.visible') - .click() - cy.findByRole('button', { name: /transactions/i }) - .should('be.visible') - .click() - - cy.selectTransactionsPanelTab(tab) - - // Waiting for transactions to be fetched - return cy.waitUntil( - () => - cy - .findByText(/Showing \d+ \w+ transactions made in/) - .should('be.visible'), - { - errorMsg: 'Failed to fetch transactions.', - timeout: 30_000, - interval: 500 - } - ) -} - export const searchAndSelectToken = ({ tokenName, tokenAddress @@ -260,8 +235,25 @@ export function switchToTransferPanelTab() { return cy.findByLabelText('Switch to Bridge Tab').click() } -export function switchToTransferHistoryTab() { - return cy.findByLabelText('Switch to Transaction History Tab').click() +export function switchToTransactionHistoryTab(tab: 'pending' | 'settled') { + cy.log(`opening transactions panel on ${tab}`) + + cy.findByLabelText('Switch to Transaction History Tab').click() + + cy.selectTransactionsPanelTab(tab) + + // Waiting for transactions to be fetched + return cy.waitUntil( + () => + cy + .findByText(/Showing \d+ \w+ transactions made in/) + .should('be.visible'), + { + errorMsg: 'Failed to fetch transactions.', + timeout: 30_000, + interval: 500 + } + ) } export function openTransactionDetails({ @@ -369,7 +361,7 @@ export function claimCctp(amount: number, options: { accept: boolean }) { const formattedAmount = formatAmount(amount, { symbol: 'USDC' }) - cy.openTransactionsPanel('pending') + cy.switchToTransactionHistoryTab('pending') cy.findTransactionInTransactionHistory({ amount, symbol: 'USDC' @@ -388,7 +380,6 @@ Cypress.Commands.addAll({ connectToApp, login, logout, - openTransactionsPanel, selectTransactionsPanelTab, searchAndSelectToken, fillCustomDestinationAddress, @@ -403,7 +394,7 @@ Cypress.Commands.addAll({ findMoveFundsButton, findSelectTokenButton, switchToTransferPanelTab, - switchToTransferHistoryTab, + switchToTransactionHistoryTab, openTransactionDetails, closeTransactionDetails, findTransactionInTransactionHistory, From 6f1fe218d505df908e45d750d2f21899af18eaf0 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 19 Nov 2024 13:38:14 +0000 Subject: [PATCH 13/49] fix e2es --- .../tests/e2e/specs/readClassicDeposits.cy.ts | 2 +- .../arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts | 4 +--- .../tests/e2e/specs/withdrawNativeToken.cy.ts | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts index c88e3caaa7..0731becf89 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts @@ -29,7 +29,7 @@ function mockClassicDepositTransaction( l2NetworkID: '42161', timestampCreated: dateYearAgo.toISOString(), timestampResolved: dateYearAgo.toISOString(), - parentToChildMsgData: { + l1ToL2MsgData: { fetchingUpdate: false, status: ParentToChildMessageStatus.NOT_YET_CREATED, retryableCreationTxID: undefined, diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts index 8165b622a3..a1ac1fb75e 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawERC20.cy.ts @@ -153,9 +153,7 @@ describe('Withdraw ERC20 Token', () => { cy.login({ networkType: 'parentChain' }) // login to L1 to claim the funds (otherwise would need to change network after clicking on claim) - cy.findByLabelText('Open Transaction History') - .should('be.visible') - .click() + cy.switchToTransactionHistoryTab('pending') cy.findClaimButton( formatAmount(ERC20AmountToSend, { diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawNativeToken.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawNativeToken.cy.ts index ac091e2966..5de34a32de 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawNativeToken.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/withdrawNativeToken.cy.ts @@ -106,9 +106,7 @@ describe('Withdraw native token', () => { // increase the timeout for this test as claim button can take ~(20 blocks *10 blocks/sec) to activate cy.login({ networkType: 'parentChain' }) // login to L1 to claim the funds (otherwise would need to change network after clicking on claim) - cy.findByLabelText('Open Transaction History') - .should('be.visible') - .click() + cy.switchToTransactionHistoryTab('pending') cy.findClaimButton( formatAmount(ETHToWithdraw, { From 1b917f3a97c3f728793234ec125e1cd908ac671f Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 19 Nov 2024 15:27:12 +0000 Subject: [PATCH 14/49] try --- .../arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts | 2 ++ packages/arb-token-bridge-ui/tests/support/commands.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts index 1c672f17f1..465bfe9eeb 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts @@ -71,6 +71,8 @@ describe('Redeem ERC20 Deposit', () => { // open transaction history and wait for deposit to fetch data cy.switchToTransactionHistoryTab('pending') + cy.wait(15_000) + // find the Retry button and the amount in the row cy.findTransactionInTransactionHistory({ amount: wethAmountToDeposit, diff --git a/packages/arb-token-bridge-ui/tests/support/commands.ts b/packages/arb-token-bridge-ui/tests/support/commands.ts index 0daf8af408..44f5a0490e 100644 --- a/packages/arb-token-bridge-ui/tests/support/commands.ts +++ b/packages/arb-token-bridge-ui/tests/support/commands.ts @@ -250,7 +250,7 @@ export function switchToTransactionHistoryTab(tab: 'pending' | 'settled') { .should('be.visible'), { errorMsg: 'Failed to fetch transactions.', - timeout: 30_000, + timeout: 60_000, interval: 500 } ) From 0074ee42f1152c85feac1111e113d3d0f751d932 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 19 Nov 2024 15:29:46 +0000 Subject: [PATCH 15/49] comment --- .../arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts index 465bfe9eeb..0473ee9391 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/redeemRetryable.cy.ts @@ -71,6 +71,7 @@ describe('Redeem ERC20 Deposit', () => { // open transaction history and wait for deposit to fetch data cy.switchToTransactionHistoryTab('pending') + // give ci more time to fetch the transactions cy.wait(15_000) // find the Retry button and the amount in the row From a6c49af78bcade22ffbeb2722c8e2d7db1f7ed58 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Mon, 25 Nov 2024 15:49:15 +0000 Subject: [PATCH 16/49] try to relaod the page --- .../tests/e2e/specs/readClassicDeposits.cy.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts index 0731becf89..a6f9b46503 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts @@ -60,6 +60,7 @@ describe('Read classic deposit messages', () => { }) ]) ) + window.location.reload() // reload the page to read the local storage cy.switchToTransactionHistoryTab('settled') @@ -91,6 +92,7 @@ describe('Read classic deposit messages', () => { }) ]) ) + window.location.reload() // reload the page to read the local storage cy.switchToTransactionHistoryTab('settled') From bc8bb0327a34f1db417c61c4af59677f98d3a058 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Mon, 25 Nov 2024 17:03:26 +0000 Subject: [PATCH 17/49] set local storage before revisiting --- .../tests/e2e/specs/readClassicDeposits.cy.ts | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts b/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts index a6f9b46503..fc06ea750a 100644 --- a/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts +++ b/packages/arb-token-bridge-ui/tests/e2e/specs/readClassicDeposits.cy.ts @@ -46,11 +46,8 @@ describe('Read classic deposit messages', () => { context('User has classic native token deposit transaction', () => { it('can read successful native token deposit', () => { - // log in to metamask - cy.login({ - networkType: 'parentChain', - networkName: 'mainnet' - }) + cy.visit('/') + window.localStorage.setItem( `arbitrum:bridge:deposits-${Cypress.env('ADDRESS').toLowerCase()}`, JSON.stringify([ @@ -60,7 +57,12 @@ describe('Read classic deposit messages', () => { }) ]) ) - window.location.reload() // reload the page to read the local storage + + // log in to metamask + cy.login({ + networkType: 'parentChain', + networkName: 'mainnet' + }) cy.switchToTransactionHistoryTab('settled') @@ -76,11 +78,8 @@ describe('Read classic deposit messages', () => { context('User has classic ERC-20 deposit transaction', () => { it('can read successful ERC-20 deposit', () => { - // log in to metamask - cy.login({ - networkType: 'parentChain', - networkName: 'mainnet' - }) + cy.visit('/') + window.localStorage.setItem( `arbitrum:bridge:deposits-${Cypress.env('ADDRESS').toLowerCase()}`, JSON.stringify([ @@ -92,7 +91,12 @@ describe('Read classic deposit messages', () => { }) ]) ) - window.location.reload() // reload the page to read the local storage + + // log in to metamask + cy.login({ + networkType: 'parentChain', + networkName: 'mainnet' + }) cy.switchToTransactionHistoryTab('settled') From 015c182d6e3ca583105e3b47ed3ab30ad91e6889 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Mon, 25 Nov 2024 18:02:25 +0000 Subject: [PATCH 18/49] try -_- --- .../arb-token-bridge-ui/tests/support/commands.ts | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/packages/arb-token-bridge-ui/tests/support/commands.ts b/packages/arb-token-bridge-ui/tests/support/commands.ts index 7d879e17e9..f6fe7efa28 100644 --- a/packages/arb-token-bridge-ui/tests/support/commands.ts +++ b/packages/arb-token-bridge-ui/tests/support/commands.ts @@ -249,18 +249,9 @@ export function switchToTransactionHistoryTab(tab: 'pending' | 'settled') { cy.selectTransactionsPanelTab(tab) - // Waiting for transactions to be fetched - return cy.waitUntil( - () => - cy - .findByText(/Showing \d+ \w+ transactions made in/) - .should('be.visible'), - { - errorMsg: 'Failed to fetch transactions.', - timeout: 120_000, - interval: 500 - } - ) + cy.findByText(/Showing \d+ \w+ transactions made in/, { + timeout: 120_000 + }).should('be.visible') } export function openTransactionDetails({ From c3b1b0ca906db520c3e536ed31b23473c839ac07 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 26 Nov 2024 11:16:22 +0000 Subject: [PATCH 19/49] remove buy panel thing --- .../src/components/MainContent/MainContent.tsx | 1 - .../arb-token-bridge-ui/src/components/TopNavBar.tsx | 9 --------- 2 files changed, 10 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 f0f39b74d4..4828edc308 100644 --- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx +++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx @@ -36,7 +36,6 @@ export function MainContent() { - {/* BUY PANEL */} diff --git a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx index 253a4ce5ed..9e2105d68d 100644 --- a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx +++ b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx @@ -27,15 +27,6 @@ export function TopNavBar() { 'grid w-full max-w-[600px] grid-cols-2 bg-white/20 p-[8px] text-white md:rounded' )} > - {/* - - Buy - */} Bridge From f3e6eeb0235eb9aed94da82cbc3fb3c147e6f967 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 26 Nov 2024 12:43:42 +0000 Subject: [PATCH 20/49] remove unnecessary code --- .../src/components/MainContent/MainContent.tsx | 4 ---- 1 file changed, 4 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 4828edc308..5078dcf745 100644 --- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx +++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx @@ -26,10 +26,6 @@ export function MainContent() { useLocalStorage(statsLocalStorageKey) const { selectedIndex, setSelectedIndex } = useSelectedTabIndex() - // without calling this, the site won't fetch the transactions and - // hence we won't be able to determine the transaction status (the dot) - useTransactionHistoryUpdater() - return ( <>
From 6af89c7fbe250788a895f509c1f5d2a35eec5bbd Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:49:40 +0000 Subject: [PATCH 21/49] address review comments --- .../components/MainContent/MainContent.tsx | 27 +++++++++++------- .../TransactionHistory/TransactionHistory.tsx | 28 +++++++++++++++++-- .../TransferPanel/TransferPanel.tsx | 4 +-- 3 files changed, 45 insertions(+), 14 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 5078dcf745..e35a6b3a1b 100644 --- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx +++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx @@ -7,29 +7,36 @@ import { ArbitrumStats, statsLocalStorageKey } from './ArbitrumStats' import { SettingsDialog } from '../common/SettingsDialog' import { TransactionHistory } from '../TransactionHistory/TransactionHistory' import { TopNavBar } from '../TopNavBar' -import { useTransactionHistoryUpdater } from '../TransactionHistory/useTransactionHistoryUpdater' -type SelectedTabIndexStore = { - selectedIndex: number - setSelectedIndex: (index: number) => void +enum MainContentTabs { + Bridge = 0, + TransactionHistory = 1 +} + +type MainContentTabStore = { + selectedTab: number + setSelectedTab: (index: number) => void + openBridge: () => void openTransactionHistory: () => void } -export const useSelectedTabIndex = create(set => ({ - selectedIndex: 0, - setSelectedIndex: (index: number) => set({ selectedIndex: index }), - openTransactionHistory: () => set({ selectedIndex: 1 }) +export const useMainContentTabs = create(set => ({ + selectedTab: MainContentTabs.Bridge, + setSelectedTab: (index: number) => set({ selectedTab: index }), + openBridge: () => set({ selectedTab: MainContentTabs.Bridge }), + openTransactionHistory: () => + set({ selectedTab: MainContentTabs.TransactionHistory }) })) export function MainContent() { const [isArbitrumStatsVisible] = useLocalStorage(statsLocalStorageKey) - const { selectedIndex, setSelectedIndex } = useSelectedTabIndex() + const { selectedTab, setSelectedTab } = useMainContentTabs() return ( <>
- + 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 45bf6c0135..f1dbc7bb2e 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx @@ -1,5 +1,5 @@ import dayjs from 'dayjs' -import { useMemo } from 'react' +import { useEffect, useMemo } from 'react' import { Tab } from '@headlessui/react' import { create } from 'zustand' import { useAccount } from 'wagmi' @@ -16,7 +16,31 @@ import { import { MergedTransaction } from '../../state/app/state' import { TabButton } from '../common/Tab' import { TransactionsTableDetails } from './TransactionsTableDetails' -import { useTransactionHistoryUpdater } from './useTransactionHistoryUpdater' +import { useTransactionHistory } from '../../hooks/useTransactionHistory' + +function useTransactionHistoryUpdater() { + const { address } = useAccount() + + const transactionHistoryProps = useTransactionHistory(address, { + runFetcher: true + }) + + const { transactions, updatePendingTransaction } = transactionHistoryProps + + const pendingTransactions = useMemo(() => { + return transactions.filter(isTxPending) + }, [transactions]) + + useEffect(() => { + const interval = setInterval(() => { + pendingTransactions.forEach(updatePendingTransaction) + }, 10_000) + + return () => clearInterval(interval) + }, [pendingTransactions, updatePendingTransaction]) + + return transactionHistoryProps +} const tabClasses = 'text-white px-3 mr-2 border-b-2 ui-selected:border-white ui-not-selected:border-transparent ui-not-selected:text-white/80 arb-hover' diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx index 77928f47d1..20d1ece3dd 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx @@ -75,7 +75,7 @@ import { MoveFundsButton } from './MoveFundsButton' import { ProjectsListing } from '../common/ProjectsListing' import { useAmountBigNumber } from './hooks/useAmountBigNumber' import { useSourceChainNativeCurrencyDecimals } from '../../hooks/useSourceChainNativeCurrencyDecimals' -import { useSelectedTabIndex } from '../MainContent/MainContent' +import { useMainContentTabs } from '../MainContent/MainContent' const signerUndefinedError = 'Signer is undefined' const transferNotAllowedError = 'Transfer not allowed' @@ -142,7 +142,7 @@ export function TransferPanel() { }) const { setTransferring } = useAppContextActions() - const { openTransactionHistory } = useSelectedTabIndex() + const { openTransactionHistory } = useMainContentTabs() const { addPendingTransaction } = useTransactionHistory(walletAddress) const isCctpTransfer = useIsCctpTransfer() From 19838c93d9f6a53e981ac42c2b3901bb7f83ff45 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Fri, 15 Nov 2024 18:07:57 +0000 Subject: [PATCH 22/49] feat: add wallet address field --- .../TransactionHistory/TransactionHistory.tsx | 11 ++- .../TransactionHistorySearchBar.tsx | 89 +++++++++++++++++++ .../TransactionHistoryTable.tsx | 4 - .../TransactionsTableDetails.tsx | 19 ++-- .../TransactionsTableDetailsSteps.tsx | 22 ++--- ...ransactionsTableDetailsTeleporterSteps.tsx | 16 +--- .../TransactionsTableRow.tsx | 3 - .../TransactionsTableRowAction.tsx | 11 ++- .../useTransactionHistoryUpdater.ts | 6 +- 9 files changed, 119 insertions(+), 62 deletions(-) create mode 100644 packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx 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 f1dbc7bb2e..7fe6ddcabf 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx @@ -1,8 +1,8 @@ import dayjs from 'dayjs' import { useEffect, useMemo } from 'react' import { Tab } from '@headlessui/react' -import { create } from 'zustand' import { useAccount } from 'wagmi' +import { create } from 'zustand' import { TransactionHistoryTable } from './TransactionHistoryTable' import { TransactionStatusInfo } from '../TransactionHistory/TransactionStatusInfo' @@ -17,6 +17,7 @@ import { MergedTransaction } from '../../state/app/state' import { TabButton } from '../common/Tab' import { TransactionsTableDetails } from './TransactionsTableDetails' import { useTransactionHistory } from '../../hooks/useTransactionHistory' +import { TransactionHistorySearchBar } from './TransactionHistorySearchBar' function useTransactionHistoryUpdater() { const { address } = useAccount() @@ -72,7 +73,6 @@ export const useTxDetailsStore = create(set => ({ })) export const TransactionHistory = () => { - const { address } = useAccount() const props = useTransactionHistoryUpdater() const { transactions } = props @@ -118,12 +118,13 @@ export const TransactionHistory = () => { return (
+ +
@@ -146,7 +147,6 @@ export const TransactionHistory = () => { { { - +
) } diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx new file mode 100644 index 0000000000..f031e6b8c2 --- /dev/null +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx @@ -0,0 +1,89 @@ +import { create } from 'zustand' +import { isAddress } from 'ethers/lib/utils.js' +import { Address, useAccount, useNetwork } from 'wagmi' + +import { Button } from '../common/Button' +import { useEffect } from 'react' +import { ExternalLink } from '../common/ExternalLink' +import { getExplorerUrl } from '../../util/networks' + +type TransactionHistoryAddressStore = { + address: string + sanitizedAddress: Address | undefined + setAddress: (address: string) => void + setSanitizedAddress: (address: string) => void +} + +export const useTransactionHistoryAddressStore = + create((set, get) => ({ + address: '', + sanitizedAddress: undefined, + setAddress: (address: string) => { + const currentSanitizedAddress = get().sanitizedAddress + + set({ address }) + + if ( + typeof currentSanitizedAddress !== 'undefined' && + isAddress(currentSanitizedAddress) && + !isAddress(address) + ) { + return + } + + get().setSanitizedAddress(address) + }, + setSanitizedAddress: (address: string) => { + if (isAddress(address)) { + set({ sanitizedAddress: address }) + } else { + set({ sanitizedAddress: undefined }) + } + } + })) + +export function TransactionHistorySearchBar() { + const { address, setAddress, setSanitizedAddress, sanitizedAddress } = + useTransactionHistoryAddressStore() + const { address: connectedAddress } = useAccount() + const { chain } = useNetwork() + + useEffect(() => { + if (address === '' && connectedAddress) { + setSanitizedAddress(connectedAddress) + } + }, [address, connectedAddress, setSanitizedAddress]) + + return ( +
+
+ setAddress(event.target.value)} + inputMode="search" + placeholder="0xWalletAddress" + aria-label="Transaction history wallet address input" + className="w-full bg-white px-3 py-1 text-sm font-light placeholder:text-gray-400" + // stop password managers from autofilling + data-1p-ignore + data-lpignore="true" + data-form-type="other" + /> + + + Showing transactions for{' '} + {chain ? ( + + {sanitizedAddress} + + ) : ( + sanitizedAddress + )} + +
+ ) +} 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 830fa9f36d..4c39cc3f3e 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx @@ -26,7 +26,6 @@ import { isTxPending } from './helpers' import { PendingDepositWarning } from './PendingDepositWarning' import { TransactionsTableRow } from './TransactionsTableRow' import { EmptyTransactionHistory } from './EmptyTransactionHistory' -import { Address } from '../../util/AddressUtils' import { MergedTransaction } from '../../state/app/state' import { useNativeCurrency } from '../../hooks/useNativeCurrency' @@ -131,7 +130,6 @@ const FailedChainPairsTooltip = ({ } type TransactionHistoryTableProps = UseTransactionHistoryResult & { - address: Address | undefined selectedTabIndex: number oldestTxTimeAgoString: string } @@ -141,7 +139,6 @@ export const TransactionHistoryTable = ( ) => { const { transactions, - address, loading, completed, error, @@ -278,7 +275,6 @@ export const TransactionHistoryTable = ( secondsPassed <= 30 && 'animate-blink bg-highlight' )} - address={address} />
) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDetails.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDetails.tsx index a4122e6275..e3802a095b 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDetails.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDetails.tsx @@ -22,12 +22,12 @@ import { GET_HELP_LINK, ether } from '../../constants' import { useTransactionHistory } from '../../hooks/useTransactionHistory' import { shortenAddress } from '../../util/CommonUtils' import { isTxCompleted } from './helpers' -import { Address } from '../../util/AddressUtils' import { sanitizeTokenSymbol } from '../../util/TokenUtils' import { isBatchTransfer } from '../../util/TokenDepositUtils' import { BatchTransferNativeTokenTooltip } from './TransactionHistoryTable' import { useNativeCurrency } from '../../hooks/useNativeCurrency' import { isCustomDestinationAddressTx } from '../../state/app/utils' +import { useTransactionHistoryAddressStore } from './TransactionHistorySearchBar' const DetailsBox = ({ children, @@ -43,14 +43,11 @@ const DetailsBox = ({ ) } -export const TransactionsTableDetails = ({ - address -}: { - address: Address | undefined -}) => { +export const TransactionsTableDetails = () => { + const { sanitizedAddress } = useTransactionHistoryAddressStore() const { tx: txFromStore, isOpen, close, reset } = useTxDetailsStore() const { ethToUSD } = useETHPrice() - const { transactions } = useTransactionHistory(address) + const { transactions } = useTransactionHistory(sanitizedAddress) const tx = useMemo(() => { if (!txFromStore) { @@ -69,7 +66,7 @@ export const TransactionsTableDetails = ({ const childProvider = getProviderForChainId(tx?.childChainId ?? 0) const nativeCurrency = useNativeCurrency({ provider: childProvider }) - if (!tx || !address || !nativeCurrency) { + if (!tx || !sanitizedAddress || !nativeCurrency) { return null } @@ -82,9 +79,9 @@ export const TransactionsTableDetails = ({ !isNetwork(tx.parentChainId).isTestnet && tx.asset === ether.symbol const isDifferentSourceAddress = - address.toLowerCase() !== tx.sender?.toLowerCase() + sanitizedAddress.toLowerCase() !== tx.sender?.toLowerCase() const isDifferentDestinationAddress = isCustomDestinationAddressTx({ - sender: address, + sender: sanitizedAddress, destination: tx.destination }) @@ -269,7 +266,7 @@ export const TransactionsTableDetails = ({ )} - + {!isTxCompleted(tx) && ( diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDetailsSteps.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDetailsSteps.tsx index a349b939ff..9e662640ab 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDetailsSteps.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDetailsSteps.tsx @@ -31,6 +31,7 @@ import { minutesToHumanReadableTime, useTransferDuration } from '../../hooks/useTransferDuration' +import { useTransactionHistoryAddressStore } from './TransactionHistorySearchBar' function needsToClaimTransfer(tx: MergedTransaction) { return tx.isCctp || tx.isWithdrawal @@ -136,14 +137,7 @@ const LastStepEndItem = ({ (!isTeleport && isDepositReadyToRedeem(tx)) || (isTeleport && secondRetryableLegForTeleportRequiresRedeem(tx)) ) { - return ( - - ) + return } return null @@ -162,13 +156,12 @@ export const TransactionFailedOnNetwork = ({ ) export const TransactionsTableDetailsSteps = ({ - tx, - address + tx }: { tx: MergedTransaction - address: Address | undefined }) => { const { approximateDurationInMinutes } = useTransferDuration(tx) + const { sanitizedAddress } = useTransactionHistoryAddressStore() const { sourceChainId } = tx @@ -240,9 +233,7 @@ export const TransactionsTableDetailsSteps = ({ /> )} - {isTeleportTx(tx) && ( - - )} + {isTeleportTx(tx) && } {/* If claiming is required we show this step */} {needsToClaimTransfer(tx) && ( @@ -256,7 +247,6 @@ export const TransactionsTableDetailsSteps = ({ type={tx.isWithdrawal ? 'withdrawals' : 'deposits'} isError={false} tx={tx} - address={address} /> ) } @@ -268,7 +258,7 @@ export const TransactionsTableDetailsSteps = ({ done={isTxCompleted(tx)} failure={isTxExpired(tx) || isDestinationChainFailure} text={destinationChainTxText} - endItem={} + endItem={} />
) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDetailsTeleporterSteps.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDetailsTeleporterSteps.tsx index 0540259aa3..a9a573b189 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDetailsTeleporterSteps.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableDetailsTeleporterSteps.tsx @@ -1,5 +1,4 @@ import { useMemo } from 'react' -import { Address } from 'wagmi' import { twMerge } from 'tailwind-merge' import { ArrowTopRightOnSquareIcon, @@ -72,11 +71,9 @@ const TeleportMiddleStepFailureExplanationNote = ({ } export const TransactionsTableDetailsTeleporterSteps = ({ - tx, - address + tx }: { tx: TeleporterMergedTransaction - address: Address | undefined }) => { const l2TxID = tx.parentToChildMsgData?.childTxId const isFirstRetryableLegSucceeded = @@ -96,15 +93,8 @@ export const TransactionsTableDetailsTeleporterSteps = ({ typeof tx.l2ToL3MsgData?.l3TxID !== 'undefined' const firstRetryableRedeemButton = useMemo( - () => ( - - ), - [tx, address] + () => , + [tx] ) const firstTransactionExternalLink = useMemo( diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRow.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRow.tsx index ea4a54db0f..01f887a666 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRow.tsx @@ -121,11 +121,9 @@ const StatusLabel = ({ tx }: { tx: MergedTransaction }) => { export function TransactionsTableRow({ tx, - address, className = '' }: { tx: MergedTransaction - address: Address | undefined className?: string }) { const { open: openTxDetails } = useTxDetailsStore() @@ -259,7 +257,6 @@ export function TransactionsTableRow({ tx={tx} isError={isError} type={tx.isWithdrawal ? 'withdrawals' : 'deposits'} - address={address} />
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 af4fe167ef..b698597a3a 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRowAction.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRowAction.tsx @@ -17,27 +17,26 @@ import { useNetwork } from 'wagmi' import { isDepositReadyToRedeem } from '../../state/app/utils' import { useRedeemRetryable } from '../../hooks/useRedeemRetryable' import { TransferCountdown } from '../common/TransferCountdown' -import { Address } from '../../util/AddressUtils' import { getChainIdForRedeemingRetryable } from '../../util/RetryableUtils' import { isTeleportTx } from '../../hooks/useTransactions' import { useRedeemTeleporter } from '../../hooks/useRedeemTeleporter' import { sanitizeTokenSymbol } from '../../util/TokenUtils' import { formatAmount } from '../../util/NumberUtils' +import { useTransactionHistoryAddressStore } from './TransactionHistorySearchBar' export function TransactionsTableRowAction({ tx, isError, - type, - address + type }: { tx: MergedTransaction | TeleporterMergedTransaction isError: boolean type: 'deposits' | 'withdrawals' - address: Address | undefined }) { const { chain } = useNetwork() const { switchNetworkAsync } = useSwitchNetworkWithConfig() const networkName = getNetworkName(chain?.id ?? 0) + const { sanitizedAddress } = useTransactionHistoryAddressStore() const tokenSymbol = sanitizeTokenSymbol(tx.asset, { erc20L1Address: tx.tokenAddress, @@ -48,10 +47,10 @@ export function TransactionsTableRowAction({ const { claim: claimCctp, isClaiming: isClaimingCctp } = useClaimCctp(tx) const { redeem, isRedeeming: isRetryableRedeeming } = useRedeemRetryable( tx, - address + sanitizedAddress ) const { redeem: teleporterRedeem, isRedeeming: isTeleporterRedeeming } = - useRedeemTeleporter(tx, address) + useRedeemTeleporter(tx, sanitizedAddress) const isRedeeming = isRetryableRedeeming || isTeleporterRedeeming diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/useTransactionHistoryUpdater.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/useTransactionHistoryUpdater.ts index 119b150dd4..968cd53e17 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/useTransactionHistoryUpdater.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/useTransactionHistoryUpdater.ts @@ -1,13 +1,13 @@ -import { useAccount } from 'wagmi' import { useTransactionHistory } from '../../hooks/useTransactionHistory' import { useEffect, useMemo } from 'react' import { isTxPending } from './helpers' +import { useTransactionHistoryAddressStore } from './TransactionHistorySearchBar' export function useTransactionHistoryUpdater() { - const { address } = useAccount() + const { sanitizedAddress } = useTransactionHistoryAddressStore() - const transactionHistoryProps = useTransactionHistory(address, { + const transactionHistoryProps = useTransactionHistory(sanitizedAddress, { runFetcher: true }) From 26646377aec04580d683537382d6ce87d6ef0384 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 19 Nov 2024 18:55:07 +0000 Subject: [PATCH 23/49] ux update --- .../TransactionHistorySearchBar.tsx | 57 +++++++++++++------ 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx index f031e6b8c2..22f60dbe1b 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx @@ -3,9 +3,11 @@ import { isAddress } from 'ethers/lib/utils.js' import { Address, useAccount, useNetwork } from 'wagmi' import { Button } from '../common/Button' -import { useEffect } from 'react' +import { useEffect, useState } from 'react' import { ExternalLink } from '../common/ExternalLink' import { getExplorerUrl } from '../../util/networks' +import { MagnifyingGlassIcon } from '@heroicons/react/24/outline' +import { twMerge } from 'tailwind-merge' type TransactionHistoryAddressStore = { address: string @@ -45,6 +47,7 @@ export const useTransactionHistoryAddressStore = export function TransactionHistorySearchBar() { const { address, setAddress, setSanitizedAddress, sanitizedAddress } = useTransactionHistoryAddressStore() + const [showAddressField, setShowAddressField] = useState(false) const { address: connectedAddress } = useAccount() const { chain } = useNetwork() @@ -56,21 +59,6 @@ export function TransactionHistorySearchBar() { return (
-
- setAddress(event.target.value)} - inputMode="search" - placeholder="0xWalletAddress" - aria-label="Transaction history wallet address input" - className="w-full bg-white px-3 py-1 text-sm font-light placeholder:text-gray-400" - // stop password managers from autofilling - data-1p-ignore - data-lpignore="true" - data-form-type="other" - /> - Showing transactions for{' '} {chain ? ( @@ -84,6 +72,43 @@ export function TransactionHistorySearchBar() { sanitizedAddress )} +
+ +
event.preventDefault()} + > + + setAddress(event.target.value)} + inputMode="search" + placeholder="0xWalletAddress" + aria-label="Transaction history wallet address input" + className="w-full bg-transparent py-1 pl-1 pr-3 text-sm font-light placeholder:text-white/60" + // stop password managers from autofilling + data-1p-ignore + data-lpignore="true" + data-form-type="other" + /> + +
) } From 923515f5a551ddff2928390f5203320f1d62e413 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Thu, 21 Nov 2024 19:04:27 +0000 Subject: [PATCH 24/49] update --- .../TransactionHistorySearchBar.tsx | 114 +++++++----------- 1 file changed, 41 insertions(+), 73 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx index 22f60dbe1b..434f907521 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx @@ -1,55 +1,39 @@ import { create } from 'zustand' import { isAddress } from 'ethers/lib/utils.js' -import { Address, useAccount, useNetwork } from 'wagmi' - -import { Button } from '../common/Button' -import { useEffect, useState } from 'react' -import { ExternalLink } from '../common/ExternalLink' -import { getExplorerUrl } from '../../util/networks' +import { Address, useAccount } from 'wagmi' +import { useCallback, useEffect } from 'react' import { MagnifyingGlassIcon } from '@heroicons/react/24/outline' import { twMerge } from 'tailwind-merge' +import { Button } from '../common/Button' + type TransactionHistoryAddressStore = { address: string sanitizedAddress: Address | undefined + searchError: any setAddress: (address: string) => void setSanitizedAddress: (address: string) => void + setSearchError: (error: any) => void } export const useTransactionHistoryAddressStore = create((set, get) => ({ address: '', sanitizedAddress: undefined, - setAddress: (address: string) => { - const currentSanitizedAddress = get().sanitizedAddress - - set({ address }) - - if ( - typeof currentSanitizedAddress !== 'undefined' && - isAddress(currentSanitizedAddress) && - !isAddress(address) - ) { - return - } - - get().setSanitizedAddress(address) - }, + setAddress: (address: string) => set({ address }), setSanitizedAddress: (address: string) => { if (isAddress(address)) { set({ sanitizedAddress: address }) - } else { - set({ sanitizedAddress: undefined }) } - } + }, + searchError: undefined, + setSearchError: (error: string | undefined) => set({ searchError: error }) })) export function TransactionHistorySearchBar() { - const { address, setAddress, setSanitizedAddress, sanitizedAddress } = + const { address, setAddress, setSanitizedAddress } = useTransactionHistoryAddressStore() - const [showAddressField, setShowAddressField] = useState(false) const { address: connectedAddress } = useAccount() - const { chain } = useNetwork() useEffect(() => { if (address === '' && connectedAddress) { @@ -57,58 +41,42 @@ export function TransactionHistorySearchBar() { } }, [address, connectedAddress, setSanitizedAddress]) + const searchTxForAddress = useCallback(() => { + if (isAddress(address)) { + setSanitizedAddress(address) + } + }, [address, setSanitizedAddress]) + return ( -
- - Showing transactions for{' '} - {chain ? ( - - {sanitizedAddress} - - ) : ( - sanitizedAddress +
+
-
+ onSubmit={event => event.preventDefault()} + > + + setAddress(event.target.value)} + inputMode="search" + placeholder="Search by address" + aria-label="Transaction history wallet address input" + className="h-full w-full bg-transparent py-1 pl-1 pr-3 text-sm font-light placeholder:text-white/60" + // stop password managers from autofilling + data-1p-ignore + data-lpignore="true" + data-form-type="other" + /> - event.preventDefault()} - > - - setAddress(event.target.value)} - inputMode="search" - placeholder="0xWalletAddress" - aria-label="Transaction history wallet address input" - className="w-full bg-transparent py-1 pl-1 pr-3 text-sm font-light placeholder:text-white/60" - // stop password managers from autofilling - data-1p-ignore - data-lpignore="true" - data-form-type="other" - /> - -
+
) } From f251bd41715a4b9d4c01f13919ff32c8ba895d27 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Fri, 22 Nov 2024 12:26:44 +0000 Subject: [PATCH 25/49] add invalid address error --- .../TransactionHistory/TransactionHistorySearchBar.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx index 434f907521..e5c8ead832 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx @@ -7,6 +7,10 @@ import { twMerge } from 'tailwind-merge' import { Button } from '../common/Button' +export enum TransactionHistorySearchError { + INVALID_ADDRESS = 'That doesn’t seem to be a valid address, please try again.' +} + type TransactionHistoryAddressStore = { address: string sanitizedAddress: Address | undefined @@ -31,7 +35,7 @@ export const useTransactionHistoryAddressStore = })) export function TransactionHistorySearchBar() { - const { address, setAddress, setSanitizedAddress } = + const { address, setAddress, setSanitizedAddress, setSearchError } = useTransactionHistoryAddressStore() const { address: connectedAddress } = useAccount() @@ -44,8 +48,10 @@ export function TransactionHistorySearchBar() { const searchTxForAddress = useCallback(() => { if (isAddress(address)) { setSanitizedAddress(address) + } else { + setSearchError(TransactionHistorySearchError.INVALID_ADDRESS) } - }, [address, setSanitizedAddress]) + }, [address, setSanitizedAddress, setSearchError]) return (
From 44fd863bb842699b971f435cfdd195f12bad5191 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:13:00 +0000 Subject: [PATCH 26/49] update error message screen --- .../EmptyTransactionHistory.tsx | 6 +- .../TransactionHistory/TransactionHistory.tsx | 102 +-------------- .../TransactionHistorySearchBar.tsx | 10 +- .../TransactionHistorySearchResults.tsx | 122 ++++++++++++++++++ .../TransactionHistoryTable.tsx | 4 +- 5 files changed, 139 insertions(+), 105 deletions(-) create mode 100644 packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchResults.tsx diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/EmptyTransactionHistory.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/EmptyTransactionHistory.tsx index 6c0d3e1b69..8a93052271 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/EmptyTransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/EmptyTransactionHistory.tsx @@ -52,5 +52,9 @@ export const EmptyTransactionHistory = ({ ) } - return Looks like no transactions here yet. + return ( + + No {tabType} transactions. + + ) } 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 7fe6ddcabf..c6f3e1ceef 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx @@ -1,23 +1,12 @@ -import dayjs from 'dayjs' import { useEffect, useMemo } from 'react' -import { Tab } from '@headlessui/react' import { useAccount } from 'wagmi' import { create } from 'zustand' -import { TransactionHistoryTable } from './TransactionHistoryTable' -import { TransactionStatusInfo } from '../TransactionHistory/TransactionStatusInfo' -import { - isTxClaimable, - isTxCompleted, - isTxExpired, - isTxFailed, - isTxPending -} from './helpers' import { MergedTransaction } from '../../state/app/state' -import { TabButton } from '../common/Tab' -import { TransactionsTableDetails } from './TransactionsTableDetails' import { useTransactionHistory } from '../../hooks/useTransactionHistory' import { TransactionHistorySearchBar } from './TransactionHistorySearchBar' +import { TransactionHistorySearchResults } from './TransactionHistorySearchResults' +import { isTxPending } from './helpers' function useTransactionHistoryUpdater() { const { address } = useAccount() @@ -73,96 +62,13 @@ export const useTxDetailsStore = create(set => ({ })) export const TransactionHistory = () => { - const props = useTransactionHistoryUpdater() - const { transactions } = props - - const oldestTxTimeAgoString = useMemo(() => { - return dayjs(transactions[transactions.length - 1]?.createdAt).toNow(true) - }, [transactions]) - - const groupedTransactions = useMemo( - () => - transactions.reduce( - (acc, tx) => { - if (isTxCompleted(tx) || isTxExpired(tx)) { - acc.settled.push(tx) - } - if (isTxPending(tx)) { - acc.pending.push(tx) - } - if (isTxClaimable(tx)) { - acc.claimable.push(tx) - } - if (isTxFailed(tx)) { - acc.failed.push(tx) - } - return acc - }, - { - settled: [] as MergedTransaction[], - pending: [] as MergedTransaction[], - claimable: [] as MergedTransaction[], - failed: [] as MergedTransaction[] - } - ), - [transactions] - ) - - const pendingTransactions = [ - ...groupedTransactions.failed, - ...groupedTransactions.pending, - ...groupedTransactions.claimable - ] - - const settledTransactions = groupedTransactions.settled + useTransactionHistoryUpdater() return (
-
- -
- - - - - Pending transactions - - - Settled transactions - - - - - - - - - - - - - +
) } diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx index e5c8ead832..df0ed4a1d1 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx @@ -42,22 +42,24 @@ export function TransactionHistorySearchBar() { useEffect(() => { if (address === '' && connectedAddress) { setSanitizedAddress(connectedAddress) + setSearchError(undefined) } - }, [address, connectedAddress, setSanitizedAddress]) + }, [address, connectedAddress, setSanitizedAddress, setSearchError]) const searchTxForAddress = useCallback(() => { - if (isAddress(address)) { + if (address !== '' && isAddress(address)) { setSanitizedAddress(address) + setSearchError(undefined) } else { setSearchError(TransactionHistorySearchError.INVALID_ADDRESS) } }, [address, setSanitizedAddress, setSearchError]) return ( -
+
event.preventDefault()} > diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchResults.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchResults.tsx new file mode 100644 index 0000000000..5d0159010e --- /dev/null +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchResults.tsx @@ -0,0 +1,122 @@ +import dayjs from 'dayjs' +import { useMemo } from 'react' +import { Tab } from '@headlessui/react' + +import { MergedTransaction } from '../../state/app/state' +import { + ContentWrapper, + TransactionHistoryTable +} from './TransactionHistoryTable' +import { TransactionStatusInfo } from '../TransactionHistory/TransactionStatusInfo' +import { + isTxClaimable, + isTxCompleted, + isTxExpired, + isTxFailed, + isTxPending +} from './helpers' +import { TabButton } from '../common/Tab' +import { TransactionsTableDetails } from './TransactionsTableDetails' +import { useTransactionHistoryUpdater } from './useTransactionHistoryUpdater' +import { useTransactionHistoryAddressStore } from './TransactionHistorySearchBar' + +const tabClasses = + 'text-white px-3 mr-2 border-b-2 ui-selected:border-white ui-not-selected:border-transparent ui-not-selected:text-white/80 arb-hover' + +export function TransactionHistorySearchResults() { + const props = useTransactionHistoryUpdater() + const { transactions } = props + const { searchError } = useTransactionHistoryAddressStore() + + const oldestTxTimeAgoString = useMemo(() => { + return dayjs(transactions[transactions.length - 1]?.createdAt).toNow(true) + }, [transactions]) + + const groupedTransactions = useMemo( + () => + transactions.reduce( + (acc, tx) => { + if (isTxCompleted(tx) || isTxExpired(tx)) { + acc.settled.push(tx) + } + if (isTxPending(tx)) { + acc.pending.push(tx) + } + if (isTxClaimable(tx)) { + acc.claimable.push(tx) + } + if (isTxFailed(tx)) { + acc.failed.push(tx) + } + return acc + }, + { + settled: [] as MergedTransaction[], + pending: [] as MergedTransaction[], + claimable: [] as MergedTransaction[], + failed: [] as MergedTransaction[] + } + ), + [transactions] + ) + + const pendingTransactions = [ + ...groupedTransactions.failed, + ...groupedTransactions.pending, + ...groupedTransactions.claimable + ] + + const settledTransactions = groupedTransactions.settled + + if (searchError) { + return ( + +

{searchError}

+
+ ) + } + + return ( + <> +
+ +
+ + + + Pending transactions + + + Settled transactions + + + + + + + + + + + + + + + ) +} 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 4c39cc3f3e..2c05ec0da8 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx @@ -53,7 +53,7 @@ export const ContentWrapper = forwardRef<
From ac33b1217d20a3bbf0e554c20ab604b53d37fd38 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:55:39 +0000 Subject: [PATCH 27/49] remove file --- .../useTransactionHistoryUpdater.ts | 29 ------------------- 1 file changed, 29 deletions(-) delete mode 100644 packages/arb-token-bridge-ui/src/components/TransactionHistory/useTransactionHistoryUpdater.ts diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/useTransactionHistoryUpdater.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/useTransactionHistoryUpdater.ts deleted file mode 100644 index 119b150dd4..0000000000 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/useTransactionHistoryUpdater.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { useAccount } from 'wagmi' -import { useTransactionHistory } from '../../hooks/useTransactionHistory' -import { useEffect, useMemo } from 'react' - -import { isTxPending } from './helpers' - -export function useTransactionHistoryUpdater() { - const { address } = useAccount() - - const transactionHistoryProps = useTransactionHistory(address, { - runFetcher: true - }) - - const { transactions, updatePendingTransaction } = transactionHistoryProps - - const pendingTransactions = useMemo(() => { - return transactions.filter(isTxPending) - }, [transactions]) - - useEffect(() => { - const interval = setInterval(() => { - pendingTransactions.forEach(updatePendingTransaction) - }, 10_000) - - return () => clearInterval(interval) - }, [pendingTransactions, updatePendingTransaction]) - - return transactionHistoryProps -} From aa9970dd2900badab69fb214be653aafad19351a Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:58:38 +0000 Subject: [PATCH 28/49] fix merge conflicts --- .../TransactionHistory/TransactionHistory.tsx | 33 ------------------- .../TransactionHistorySearchResults.tsx | 29 ++++++++++++++-- 2 files changed, 27 insertions(+), 35 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 c6f3e1ceef..b1166da64a 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx @@ -1,39 +1,8 @@ -import { useEffect, useMemo } from 'react' -import { useAccount } from 'wagmi' import { create } from 'zustand' import { MergedTransaction } from '../../state/app/state' -import { useTransactionHistory } from '../../hooks/useTransactionHistory' import { TransactionHistorySearchBar } from './TransactionHistorySearchBar' import { TransactionHistorySearchResults } from './TransactionHistorySearchResults' -import { isTxPending } from './helpers' - -function useTransactionHistoryUpdater() { - const { address } = useAccount() - - const transactionHistoryProps = useTransactionHistory(address, { - runFetcher: true - }) - - const { transactions, updatePendingTransaction } = transactionHistoryProps - - const pendingTransactions = useMemo(() => { - return transactions.filter(isTxPending) - }, [transactions]) - - useEffect(() => { - const interval = setInterval(() => { - pendingTransactions.forEach(updatePendingTransaction) - }, 10_000) - - return () => clearInterval(interval) - }, [pendingTransactions, updatePendingTransaction]) - - return transactionHistoryProps -} - -const tabClasses = - 'text-white px-3 mr-2 border-b-2 ui-selected:border-white ui-not-selected:border-transparent ui-not-selected:text-white/80 arb-hover' type TxDetailsStore = { tx: MergedTransaction | null @@ -62,8 +31,6 @@ export const useTxDetailsStore = create(set => ({ })) export const TransactionHistory = () => { - useTransactionHistoryUpdater() - return (
diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchResults.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchResults.tsx index 5d0159010e..f973fc9e27 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchResults.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchResults.tsx @@ -1,5 +1,6 @@ import dayjs from 'dayjs' -import { useMemo } from 'react' +import { useEffect, useMemo } from 'react' +import { useAccount } from 'wagmi' import { Tab } from '@headlessui/react' import { MergedTransaction } from '../../state/app/state' @@ -17,9 +18,33 @@ import { } from './helpers' import { TabButton } from '../common/Tab' import { TransactionsTableDetails } from './TransactionsTableDetails' -import { useTransactionHistoryUpdater } from './useTransactionHistoryUpdater' +import { useTransactionHistory } from '../../hooks/useTransactionHistory' import { useTransactionHistoryAddressStore } from './TransactionHistorySearchBar' +function useTransactionHistoryUpdater() { + const { address } = useAccount() + + const transactionHistoryProps = useTransactionHistory(address, { + runFetcher: true + }) + + const { transactions, updatePendingTransaction } = transactionHistoryProps + + const pendingTransactions = useMemo(() => { + return transactions.filter(isTxPending) + }, [transactions]) + + useEffect(() => { + const interval = setInterval(() => { + pendingTransactions.forEach(updatePendingTransaction) + }, 10_000) + + return () => clearInterval(interval) + }, [pendingTransactions, updatePendingTransaction]) + + return transactionHistoryProps +} + const tabClasses = 'text-white px-3 mr-2 border-b-2 ui-selected:border-white ui-not-selected:border-transparent ui-not-selected:text-white/80 arb-hover' From 9be68c491ac38dfe3b2f234533adb8520d1d8366 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:15:38 +0000 Subject: [PATCH 29/49] use enum as type --- .../src/components/MainContent/MainContent.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 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 e35a6b3a1b..72e108efa6 100644 --- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx +++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx @@ -14,15 +14,15 @@ enum MainContentTabs { } type MainContentTabStore = { - selectedTab: number - setSelectedTab: (index: number) => void + selectedTab: MainContentTabs + setSelectedTab: (index: MainContentTabs) => void openBridge: () => void openTransactionHistory: () => void } export const useMainContentTabs = create(set => ({ selectedTab: MainContentTabs.Bridge, - setSelectedTab: (index: number) => set({ selectedTab: index }), + setSelectedTab: (index: MainContentTabs) => set({ selectedTab: index }), openBridge: () => set({ selectedTab: MainContentTabs.Bridge }), openTransactionHistory: () => set({ selectedTab: MainContentTabs.TransactionHistory }) From 0d90fa1d332487eca0792b8d706480f359bfc7ce Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:28:49 +0000 Subject: [PATCH 30/49] update name --- .../src/components/MainContent/MainContent.tsx | 8 ++++---- .../src/components/TransferPanel/TransferPanel.tsx | 6 +++--- 2 files changed, 7 insertions(+), 7 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 72e108efa6..b8d2718dd7 100644 --- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx +++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx @@ -16,15 +16,15 @@ enum MainContentTabs { type MainContentTabStore = { selectedTab: MainContentTabs setSelectedTab: (index: MainContentTabs) => void - openBridge: () => void - openTransactionHistory: () => void + switchToBridgeTab: () => void + switchToTransactionHistoryTab: () => void } export const useMainContentTabs = create(set => ({ selectedTab: MainContentTabs.Bridge, setSelectedTab: (index: MainContentTabs) => set({ selectedTab: index }), - openBridge: () => set({ selectedTab: MainContentTabs.Bridge }), - openTransactionHistory: () => + switchToBridgeTab: () => set({ selectedTab: MainContentTabs.Bridge }), + switchToTransactionHistoryTab: () => set({ selectedTab: MainContentTabs.TransactionHistory }) })) diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx index 20d1ece3dd..5710d4ba57 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx @@ -142,7 +142,7 @@ export function TransferPanel() { }) const { setTransferring } = useAppContextActions() - const { openTransactionHistory } = useMainContentTabs() + const { switchToTransactionHistoryTab } = useMainContentTabs() const { addPendingTransaction } = useTransactionHistory(walletAddress) const isCctpTransfer = useIsCctpTransfer() @@ -498,7 +498,7 @@ export function TransferPanel() { } addPendingTransaction(newTransfer) - openTransactionHistory() + switchToTransactionHistoryTab() setTransferring(false) clearAmountInput() } catch (e) { @@ -837,7 +837,7 @@ export function TransferPanel() { ) } - openTransactionHistory() + switchToTransactionHistoryTab() setTransferring(false) clearAmountInput() From 468c54646e971ed65407a69785c146acae21cd27 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:58:22 +0000 Subject: [PATCH 31/49] fix merge conflict dropped code --- .../TransactionHistory/TransactionHistorySearchResults.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchResults.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchResults.tsx index f973fc9e27..012cbe2146 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchResults.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchResults.tsx @@ -1,6 +1,5 @@ import dayjs from 'dayjs' import { useEffect, useMemo } from 'react' -import { useAccount } from 'wagmi' import { Tab } from '@headlessui/react' import { MergedTransaction } from '../../state/app/state' @@ -22,9 +21,9 @@ import { useTransactionHistory } from '../../hooks/useTransactionHistory' import { useTransactionHistoryAddressStore } from './TransactionHistorySearchBar' function useTransactionHistoryUpdater() { - const { address } = useAccount() + const { sanitizedAddress } = useTransactionHistoryAddressStore() - const transactionHistoryProps = useTransactionHistory(address, { + const transactionHistoryProps = useTransactionHistory(sanitizedAddress, { runFetcher: true }) From c95f81b51dce7be3c7e71de5b406e4d469a77d2c Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:29:42 +0000 Subject: [PATCH 32/49] address review comments --- .../TransactionHistorySearchBar.tsx | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx index df0ed4a1d1..f6486fcc35 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx @@ -14,14 +14,14 @@ export enum TransactionHistorySearchError { type TransactionHistoryAddressStore = { address: string sanitizedAddress: Address | undefined - searchError: any + searchError: TransactionHistorySearchError | undefined setAddress: (address: string) => void setSanitizedAddress: (address: string) => void - setSearchError: (error: any) => void + setSearchError: (error: TransactionHistorySearchError | undefined) => void } export const useTransactionHistoryAddressStore = - create((set, get) => ({ + create(set => ({ address: '', sanitizedAddress: undefined, setAddress: (address: string) => set({ address }), @@ -31,7 +31,8 @@ export const useTransactionHistoryAddressStore = } }, searchError: undefined, - setSearchError: (error: string | undefined) => set({ searchError: error }) + setSearchError: (error: TransactionHistorySearchError | undefined) => + set({ searchError: error }) })) export function TransactionHistorySearchBar() { @@ -47,12 +48,17 @@ export function TransactionHistorySearchBar() { }, [address, connectedAddress, setSanitizedAddress, setSearchError]) const searchTxForAddress = useCallback(() => { - if (address !== '' && isAddress(address)) { - setSanitizedAddress(address) - setSearchError(undefined) - } else { + if (address === '') { + return + } + + if (!isAddress(address)) { setSearchError(TransactionHistorySearchError.INVALID_ADDRESS) + return } + + setSanitizedAddress(address) + setSearchError(undefined) }, [address, setSanitizedAddress, setSearchError]) return ( @@ -79,8 +85,13 @@ export function TransactionHistorySearchBar() { /> From 5e36d697360fa18397c9866ea60aa5926fe47cc9 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 26 Nov 2024 19:21:31 +0000 Subject: [PATCH 33/49] feat: search by tx hash --- .../TransactionHistorySearchBar.tsx | 54 +++++++++++++------ .../components/TransactionHistory/helpers.ts | 46 +++++++++++++++- 2 files changed, 82 insertions(+), 18 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx index f6486fcc35..3ad6eecfd1 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx @@ -6,60 +6,82 @@ import { MagnifyingGlassIcon } from '@heroicons/react/24/outline' import { twMerge } from 'tailwind-merge' import { Button } from '../common/Button' +import { isValidTxHash } from './helpers' export enum TransactionHistorySearchError { INVALID_ADDRESS = 'That doesn’t seem to be a valid address, please try again.' } type TransactionHistoryAddressStore = { - address: string + addressOrTxHash: string sanitizedAddress: Address | undefined + sanitizedTxHash: string | undefined searchError: TransactionHistorySearchError | undefined - setAddress: (address: string) => void + setAddressOrTxHash: (addressOrTxHash: string) => void setSanitizedAddress: (address: string) => void + setSanitizedTxHash: (txHash: string) => void setSearchError: (error: TransactionHistorySearchError | undefined) => void } export const useTransactionHistoryAddressStore = create(set => ({ - address: '', + addressOrTxHash: '', sanitizedAddress: undefined, - setAddress: (address: string) => set({ address }), + setAddressOrTxHash: (addressOrTxHash: string) => set({ addressOrTxHash }), setSanitizedAddress: (address: string) => { if (isAddress(address)) { set({ sanitizedAddress: address }) } }, + sanitizedTxHash: undefined, + setSanitizedTxHash: (txHash: string) => { + if (isValidTxHash(txHash)) { + set({ + sanitizedTxHash: txHash + }) + } + }, searchError: undefined, setSearchError: (error: TransactionHistorySearchError | undefined) => set({ searchError: error }) })) export function TransactionHistorySearchBar() { - const { address, setAddress, setSanitizedAddress, setSearchError } = - useTransactionHistoryAddressStore() + const { + addressOrTxHash, + setAddressOrTxHash, + setSanitizedAddress, + setSanitizedTxHash, + setSearchError + } = useTransactionHistoryAddressStore() const { address: connectedAddress } = useAccount() useEffect(() => { - if (address === '' && connectedAddress) { + if (addressOrTxHash === '' && connectedAddress) { setSanitizedAddress(connectedAddress) setSearchError(undefined) } - }, [address, connectedAddress, setSanitizedAddress, setSearchError]) + }, [addressOrTxHash, connectedAddress, setSanitizedAddress, setSearchError]) const searchTxForAddress = useCallback(() => { - if (address === '') { + if (addressOrTxHash === '') { + return + } + + if (isValidTxHash(addressOrTxHash)) { + setSanitizedTxHash(addressOrTxHash) + setSearchError(undefined) return } - if (!isAddress(address)) { + if (!isAddress(addressOrTxHash)) { setSearchError(TransactionHistorySearchError.INVALID_ADDRESS) return } - setSanitizedAddress(address) + setSanitizedAddress(addressOrTxHash) setSearchError(undefined) - }, [address, setSanitizedAddress, setSearchError]) + }, [addressOrTxHash, setSanitizedAddress, setSanitizedTxHash, setSearchError]) return (
@@ -72,10 +94,10 @@ export function TransactionHistorySearchBar() { setAddress(event.target.value)} + value={addressOrTxHash} + onChange={event => setAddressOrTxHash(event.target.value)} inputMode="search" - placeholder="Search by address" + placeholder="Search by address or transaction hash" aria-label="Transaction history wallet address input" className="h-full w-full bg-transparent py-1 pl-1 pr-3 text-sm font-light placeholder:text-white/60" // stop password managers from autofilling @@ -91,7 +113,7 @@ export function TransactionHistorySearchBar() { 'disabled:border-y-0 disabled:border-r-0 disabled:border-l-gray-dark' )} onClick={searchTxForAddress} - disabled={!address} + disabled={!addressOrTxHash} > Search 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 bc44d3cec3..6e982ebb46 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts @@ -6,7 +6,8 @@ import { ParentToChildMessageStatus, ParentToChildMessageReader, ChildTransactionReceipt, - ChildToParentTransactionEvent + ChildToParentTransactionEvent, + ParentTransactionReceipt } from '@arbitrum/sdk' import { @@ -15,7 +16,12 @@ import { TeleporterMergedTransaction, WithdrawalStatus } from '../../state/app/state' -import { ChainId, getL1BlockTime, isNetwork } from '../../util/networks' +import { + ChainId, + getL1BlockTime, + isNetwork, + supportedCustomOrbitParentChains +} from '../../util/networks' import { Deposit, Transfer } from '../../hooks/useTransactionHistory' import { getParentToChildMessageDataFromParentTxHash, @@ -602,3 +608,39 @@ export function getDestinationNetworkTxId(tx: MergedTransaction) { ? tx.childToParentMsgData?.uniqueId.toString() : tx.parentToChildMsgData?.childTxId } + +export const supportedParentChains = [ + ChainId.Ethereum, + ChainId.ArbitrumOne, + ChainId.ArbitrumNova, + ChainId.Base, + ...supportedCustomOrbitParentChains +] + +export async function getParentTxReceipt( + txHash: string +): Promise { + const promises = Object.entries(supportedParentChains).map( + async ([chainId]) => { + try { + const l1Provider = getProviderForChainId(Number(chainId)) + + const receipt = await l1Provider.getTransactionReceipt(txHash) + if (receipt) { + return new ParentTransactionReceipt(receipt) + } + } catch (e) { + console.warn(`Cannot get tx receipt from parent chain ${chainId}`) + } + } + ) + const results = await Promise.all(promises) + return results.find(r => r) +} + +export function isValidTxHash(txHash: string | undefined): txHash is string { + if (!txHash) { + return false + } + return /^0x([A-Fa-f0-9]{64})$/.test(txHash) +} From ac5acd0316934e61e4db58eb4e4ec5447a9bfd97 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Wed, 27 Nov 2024 19:05:56 +0000 Subject: [PATCH 34/49] wip --- .../TransactionHistory/TransactionHistory.tsx | 7 +- ...ransactionHistoryAddressSearchResults.tsx} | 4 +- .../TransactionHistoryTable.tsx | 5 +- .../TransactionHistoryTxHashSearchResult.tsx | 192 ++++++++++++++++++ .../TransactionHistoryTxHashSearch/helpers.ts | 169 +++++++++++++++ .../TransactionHistory/constants.ts | 2 + 6 files changed, 372 insertions(+), 7 deletions(-) rename packages/arb-token-bridge-ui/src/components/TransactionHistory/{TransactionHistorySearchResults.tsx => TransactionHistoryAddressSearchResults.tsx} (96%) create mode 100644 packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx create mode 100644 packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts create mode 100644 packages/arb-token-bridge-ui/src/components/TransactionHistory/constants.ts 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 b1166da64a..28b6292467 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx @@ -2,7 +2,8 @@ import { create } from 'zustand' import { MergedTransaction } from '../../state/app/state' import { TransactionHistorySearchBar } from './TransactionHistorySearchBar' -import { TransactionHistorySearchResults } from './TransactionHistorySearchResults' +import { TransactionHistoryAddressSearchResults } from './TransactionHistoryAddressSearchResults' +import { TransactionHistoryTxHashSearchResult } from './TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult' type TxDetailsStore = { tx: MergedTransaction | null @@ -35,7 +36,9 @@ export const TransactionHistory = () => {
- + + +
) } diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchResults.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryAddressSearchResults.tsx similarity index 96% rename from packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchResults.tsx rename to packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryAddressSearchResults.tsx index 012cbe2146..241d4ef948 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchResults.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryAddressSearchResults.tsx @@ -7,7 +7,7 @@ import { ContentWrapper, TransactionHistoryTable } from './TransactionHistoryTable' -import { TransactionStatusInfo } from '../TransactionHistory/TransactionStatusInfo' +import { TransactionStatusInfo } from './TransactionStatusInfo' import { isTxClaimable, isTxCompleted, @@ -47,7 +47,7 @@ function useTransactionHistoryUpdater() { const tabClasses = 'text-white px-3 mr-2 border-b-2 ui-selected:border-white ui-not-selected:border-transparent ui-not-selected:text-white/80 arb-hover' -export function TransactionHistorySearchResults() { +export function TransactionHistoryAddressSearchResults() { const props = useTransactionHistoryUpdater() const { transactions } = props const { searchError } = useTransactionHistoryAddressStore() 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 2c05ec0da8..3f04090742 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTable.tsx @@ -28,6 +28,7 @@ import { TransactionsTableRow } from './TransactionsTableRow' import { EmptyTransactionHistory } from './EmptyTransactionHistory' import { MergedTransaction } from '../../state/app/state' import { useNativeCurrency } from '../../hooks/useNativeCurrency' +import { TABLE_HEADER_HEIGHT, TABLE_ROW_HEIGHT } from './constants' export const BatchTransferNativeTokenTooltip = ({ children, @@ -65,7 +66,7 @@ export const ContentWrapper = forwardRef< ContentWrapper.displayName = 'ContentWrapper' -const TableHeader = ({ +export const TableHeader = ({ children, className }: PropsWithChildren<{ className?: string }>) => ( @@ -148,8 +149,6 @@ export const TransactionHistoryTable = ( oldestTxTimeAgoString } = props - const TABLE_HEADER_HEIGHT = 52 - const TABLE_ROW_HEIGHT = 60 const isTxHistoryEmpty = transactions.length === 0 const isPendingTab = selectedTabIndex === 0 diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx new file mode 100644 index 0000000000..f487ab05bf --- /dev/null +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx @@ -0,0 +1,192 @@ +import { Column, Table } from 'react-virtualized' +import { useMemo, useRef } from 'react' +import { twMerge } from 'tailwind-merge' +import { ParentTransactionReceipt } from '@arbitrum/sdk' + +import { ContentWrapper, TableHeader } from '../TransactionHistoryTable' +import { TransactionsTableRow } from '../TransactionsTableRow' +import { getParentTxReceipt } from '../helpers' +import { getChildToParentMessages, ReceiptState } from './helpers' + +async function getData(txHash: string) { + const parentTxReceipt = await getParentTxReceipt(txHash) + const defaultReturn: { + allMessages: L1ToL2MessagesAndDepositMessages + l2ToL1MessagesToShow: L2ToL1MessageData[] + parentTxReceipt: ParentTransactionReceipt | undefined + } = { + allMessages: { + retryables: [], + retryablesClassic: [], + deposits: [] + }, + l2ToL1MessagesToShow: [], + parentTxReceipt + } + + if (parentTxReceipt === undefined) { + const res = await getChildToParentMessages(txHash) + const { ChildTxStatus, l2ToL1Messages } = res + + // TODO: handle terminal states + if (l2ToL1Messages.length > 0) { + return { + ...defaultReturn, + parentTxReceipt, + txHashState: ReceiptState.MESSAGES_FOUND, + l2ToL1MessagesToShow: l2ToL1Messages + } + } + if (ChildTxStatus === ChildTxStatus.SUCCESS) { + return { + ...defaultReturn, + parentTxReceipt, + txHashState: ReceiptState.NO_L2_L1_MESSAGES + } + } + if (ChildTxStatus === ChildTxStatus.FAILURE) { + return { + ...defaultReturn, + parentTxReceipt, + txHashState: ReceiptState.L2_FAILED + } + } + + return { + ...defaultReturn, + parentTxReceipt, + txHashState: ReceiptState.NOT_FOUND + } + } + + const { l1TxnReceipt: _l1TxnReceipt, l1Network } = receiptRes + if (_l1TxnReceipt.status === 0) { + return { + ...defaultReturn, + l1TxnReceipt, + txHashState: ReceiptState.L1_FAILED + } + } + + const allMessages = await getL1ToL2MessagesAndDepositMessages( + _l1TxnReceipt, + l1Network + ) + const l1ToL2Messages = allMessages.retryables + const l1ToL2MessagesClassic = allMessages.retryablesClassic + const depositMessages = allMessages.deposits + if ( + l1ToL2Messages.length === 0 && + l1ToL2MessagesClassic.length === 0 && + depositMessages.length === 0 + ) { + return { + ...defaultReturn, + l1TxnReceipt, + txHashState: ReceiptState.NO_L1_L2_MESSAGES + } + } + + return { + ...defaultReturn, + allMessages, + l1TxnReceipt, + receiptRes, + txHashState: ReceiptState.MESSAGES_FOUND + } +} + +export function TransactionHistoryTxHashSearchResult() { + const contentWrapperRef = useRef(null) + const tableRef = useRef
(null) + + const TABLE_HEADER_HEIGHT = 52 + const TABLE_ROW_HEIGHT = 60 + + const tableHeight = useMemo(() => { + if (window.innerWidth < 768) { + return TABLE_ROW_HEIGHT * (transactions.length + 1) + TABLE_HEADER_HEIGHT + } + const SIDE_PANEL_HEADER_HEIGHT = 125 + const viewportHeight = window.innerHeight + const contentWrapperOffsetTop = contentWrapperRef.current?.offsetTop ?? 0 + return Math.max( + // we subtract a little padding at the end so that the table doesn't end at the edge of the screen + viewportHeight - contentWrapperOffsetTop - SIDE_PANEL_HEADER_HEIGHT, + 0 + ) + }, [contentWrapperRef.current?.offsetTop, transactions.length]) + + return ( + +
( +
+ {props.columns} +
+ )} + className="table-auto last:border-b-0" + rowGetter={({ index }) => transactions[index]} + rowRenderer={({ index, style }) => { + const tx = transactions[index] + + if (!tx) { + return null + } + + const isLastRow = index + 1 === transactions.length + const key = `${tx.parentChainId}-${tx.childChainId}-${tx.txId}` + + return ( +
+ +
+ ) + }} + > + TIME} + /> + TOKEN} + /> + FROM} + /> + TO} + /> + STATUS} + /> +
+ + ) +} diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts new file mode 100644 index 0000000000..5615cb4719 --- /dev/null +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts @@ -0,0 +1,169 @@ +import { BigNumber } from 'ethers' +import { + ArbitrumNetwork, + ChildToParentMessage, + ChildToParentMessageReader, + ChildToParentMessageStatus, + ChildTransactionReceipt, + getArbitrumNetwork, + getArbitrumNetworks +} from '@arbitrum/sdk' + +import { getProviderForChainId } from '@/token-bridge-sdk/utils' +import { supportedParentChains } from '../helpers' +import { JsonRpcProvider } from '@ethersproject/providers' + +export enum ChildTxStatus { + SUCCESS = 'SUCCESS', + FAILURE = 'FAILURE', + NOT_FOUND = 'NOT_FOUND' +} + +export enum ReceiptState { + EMPTY, + LOADING, + INVALID_INPUT_LENGTH, + NOT_FOUND, + L1_FAILED, + L2_FAILED, + NO_L1_L2_MESSAGES, + MESSAGES_FOUND, + NO_L2_L1_MESSAGES +} + +export interface ChildToParentMessageData { + status: ChildToParentMessageStatus + childToParentMessage: ChildToParentMessage + confirmationInfo: { + deadlineBlock: BigNumber + etaSeconds: number + } | null + childNetwork: ArbitrumNetwork + childProvider: JsonRpcProvider + createdAtChildBlockNumber: number + childToParentEventIndex: number +} + +export interface ChildToParentMessageSearchResult { + childTxStatus: ChildTxStatus + childToParentMessages: ChildToParentMessageData[] + childTxHash: string +} + +export const getChildToParentMessages = async ( + txHash: string +): Promise => { + return new Promise(async resolve => { + const supportedChildChainIds = getArbitrumNetworks() + const messagesPromises = supportedChildChainIds.map(async ({ chainId }) => { + const childNetwork = await getArbitrumNetwork(Number(chainId)) + const childProvider = getProviderForChainId(Number(chainId)) + + const parentChainId = + childNetwork.parentChainId as unknown as keyof typeof supportedParentChains + const parentProvider = getProviderForChainId(Number(parentChainId)) + try { + await parentProvider.getBlockNumber() + } catch (e) { + console.warn(supportedParentChains[parentChainId], 'not working') + return null + } + const [l1BlogNumber, receipt] = await Promise.all([ + parentProvider.getBlockNumber(), + childProvider.getTransactionReceipt(txHash) + ]) + const currentL1Block = BigNumber.from(l1BlogNumber) + if (!receipt) { + return null + } + if (receipt.status === 0) { + // l1 tx failed, terminal + resolve({ + childTxStatus: ChildTxStatus.FAILURE, + childToParentMessages: [], + childTxHash: txHash + }) + } + + const l2Receipt = new ChildTransactionReceipt(receipt) + const l2ToL1Events = l2Receipt.getChildToParentEvents() + const l2MessagesData: Promise[] = + l2ToL1Events.map( + async (childToParentEvent, childToParentEventIndex) => { + const childToParentMessage = new ChildToParentMessageReader( + parentProvider, + childToParentEvent + ) + try { + const status = await childToParentMessage.status(childProvider) + const deadlineBlock = + status !== ChildToParentMessageStatus.CONFIRMED && + status !== ChildToParentMessageStatus.EXECUTED + ? await childToParentMessage.getFirstExecutableBlock( + childProvider + ) + : null + return { + status, + childToParentMessage, + confirmationInfo: deadlineBlock + ? { + deadlineBlock, + etaSeconds: deadlineBlock + .sub(currentL1Block) + .mul(12) + .toNumber() + } + : null, + childNetwork, + childProvider, + createdAtChildBlockNumber: l2Receipt.blockNumber, + childToParentEventIndex + } + } catch (e) { + const expectedError = "batch doesn't exist" + const err = e as Error & { error: Error } + const actualError = + err && (err.message || (err.error && err.error.message)) + if (actualError.includes(expectedError)) { + console.warn('batch doesnt exist') + + return { + status: ChildToParentMessageStatus.UNCONFIRMED, + childToParentMessage, + confirmationInfo: null, + childNetwork, + childProvider, + createdAtChildBlockNumber: l2Receipt.blockNumber, + childToParentEventIndex + } + } else { + throw e + } + } + } + ) + + return await Promise.all(l2MessagesData) + }) + + const data = await Promise.all(messagesPromises) + const messages = data + .flatMap(d => d) + .filter(d => d) as ChildToParentMessageData[] + + if (messages.length) { + resolve({ + childTxStatus: ChildTxStatus.SUCCESS, + childToParentMessages: messages, + childTxHash: txHash + }) + } else { + resolve({ + childTxStatus: ChildTxStatus.NOT_FOUND, + childToParentMessages: [], + childTxHash: txHash + }) + } + }) +} diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/constants.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/constants.ts new file mode 100644 index 0000000000..db204f91c4 --- /dev/null +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/constants.ts @@ -0,0 +1,2 @@ +export const TABLE_HEADER_HEIGHT = 52 +export const TABLE_ROW_HEIGHT = 60 From 009d66f57e10a9bcac60171fdbf0ae67ebe8883f Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Thu, 28 Nov 2024 17:56:12 +0000 Subject: [PATCH 35/49] wip --- .../TransactionHistory/TransactionHistory.tsx | 2 +- .../TransactionHistoryTxHashSearchResult.tsx | 115 +++++++++++++----- ...ParentToChildMessagesAndDepositMessages.ts | 91 ++++++++++++++ .../TransactionHistoryTxHashSearch/helpers.ts | 25 +++- .../components/TransactionHistory/helpers.ts | 31 +++-- 5 files changed, 222 insertions(+), 42 deletions(-) create mode 100644 packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages.ts 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 28b6292467..2066bc85eb 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx @@ -38,7 +38,7 @@ export const TransactionHistory = () => { - + {/* */}
) } diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx index f487ab05bf..2513f50214 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx @@ -1,18 +1,33 @@ import { Column, Table } from 'react-virtualized' -import { useMemo, useRef } from 'react' +import { useEffect, useMemo, useRef } from 'react' import { twMerge } from 'tailwind-merge' import { ParentTransactionReceipt } from '@arbitrum/sdk' +import useSWR from 'swr' import { ContentWrapper, TableHeader } from '../TransactionHistoryTable' import { TransactionsTableRow } from '../TransactionsTableRow' import { getParentTxReceipt } from '../helpers' -import { getChildToParentMessages, ReceiptState } from './helpers' +import { + ChildToParentMessageData, + ChildTxStatus, + getChildToParentMessages, + ParentToChildMessagesAndDepositMessages, + ReceiptState +} from './helpers' +import { getParentToChildMessagesAndDepositMessages } from './getParentToChildMessagesAndDepositMessages' +import { useTransactionHistoryAddressStore } from '../TransactionHistorySearchBar' -async function getData(txHash: string) { - const parentTxReceipt = await getParentTxReceipt(txHash) +async function getData(txHash: string | undefined) { + console.log('in getData: ', txHash) + if (!txHash) { + return + } + + const getParentTxReceiptResult = await getParentTxReceipt(txHash) + const parentTxReceipt = getParentTxReceiptResult const defaultReturn: { - allMessages: L1ToL2MessagesAndDepositMessages - l2ToL1MessagesToShow: L2ToL1MessageData[] + allMessages: ParentToChildMessagesAndDepositMessages + l2ToL1MessagesToShow: ChildToParentMessageData[] parentTxReceipt: ParentTransactionReceipt | undefined } = { allMessages: { @@ -21,30 +36,30 @@ async function getData(txHash: string) { deposits: [] }, l2ToL1MessagesToShow: [], - parentTxReceipt + parentTxReceipt: getParentTxReceiptResult?.parentTxReceipt } - if (parentTxReceipt === undefined) { + if (getParentTxReceiptResult === undefined) { const res = await getChildToParentMessages(txHash) - const { ChildTxStatus, l2ToL1Messages } = res + const { childTxStatus, childToParentMessages } = res // TODO: handle terminal states - if (l2ToL1Messages.length > 0) { + if (childToParentMessages.length > 0) { return { ...defaultReturn, parentTxReceipt, txHashState: ReceiptState.MESSAGES_FOUND, - l2ToL1MessagesToShow: l2ToL1Messages + l2ToL1MessagesToShow: childToParentMessages } } - if (ChildTxStatus === ChildTxStatus.SUCCESS) { + if (childTxStatus === ChildTxStatus.SUCCESS) { return { ...defaultReturn, parentTxReceipt, txHashState: ReceiptState.NO_L2_L1_MESSAGES } } - if (ChildTxStatus === ChildTxStatus.FAILURE) { + if (childTxStatus === ChildTxStatus.FAILURE) { return { ...defaultReturn, parentTxReceipt, @@ -59,18 +74,22 @@ async function getData(txHash: string) { } } - const { l1TxnReceipt: _l1TxnReceipt, l1Network } = receiptRes - if (_l1TxnReceipt.status === 0) { + const { parentTxReceipt: _parentTxReceipt, parentChainId } = + getParentTxReceiptResult + if ( + _parentTxReceipt?.status === 0 || + typeof _parentTxReceipt === 'undefined' + ) { return { ...defaultReturn, - l1TxnReceipt, + parentTxReceipt, txHashState: ReceiptState.L1_FAILED } } - const allMessages = await getL1ToL2MessagesAndDepositMessages( - _l1TxnReceipt, - l1Network + const allMessages = await getParentToChildMessagesAndDepositMessages( + _parentTxReceipt, + parentChainId ) const l1ToL2Messages = allMessages.retryables const l1ToL2MessagesClassic = allMessages.retryablesClassic @@ -82,7 +101,7 @@ async function getData(txHash: string) { ) { return { ...defaultReturn, - l1TxnReceipt, + parentTxReceipt, txHashState: ReceiptState.NO_L1_L2_MESSAGES } } @@ -90,8 +109,7 @@ async function getData(txHash: string) { return { ...defaultReturn, allMessages, - l1TxnReceipt, - receiptRes, + parentTxReceipt, txHashState: ReceiptState.MESSAGES_FOUND } } @@ -99,13 +117,32 @@ async function getData(txHash: string) { export function TransactionHistoryTxHashSearchResult() { const contentWrapperRef = useRef(null) const tableRef = useRef(null) + const { sanitizedTxHash } = useTransactionHistoryAddressStore() + const queryKey = useMemo(() => { + if (!sanitizedTxHash) { + return null + } + return [sanitizedTxHash, 'TransactionHistoryTxHashSearchResult'] as const + }, [sanitizedTxHash]) + const { + data: getTxDataResult, + isLoading, + error + } = useSWR(queryKey, ([_txHash]) => getData(_txHash), { + refreshInterval: 30_000, + shouldRetryOnError: true + }) + + console.log('getTxDataResult: ', getTxDataResult) const TABLE_HEADER_HEIGHT = 52 const TABLE_ROW_HEIGHT = 60 const tableHeight = useMemo(() => { + const transactionLength = 1 + if (window.innerWidth < 768) { - return TABLE_ROW_HEIGHT * (transactions.length + 1) + TABLE_HEADER_HEIGHT + return TABLE_ROW_HEIGHT * (transactionLength + 1) + TABLE_HEADER_HEIGHT } const SIDE_PANEL_HEADER_HEIGHT = 125 const viewportHeight = window.innerHeight @@ -115,7 +152,23 @@ export function TransactionHistoryTxHashSearchResult() { viewportHeight - contentWrapperOffsetTop - SIDE_PANEL_HEADER_HEIGHT, 0 ) - }, [contentWrapperRef.current?.offsetTop, transactions.length]) + }, [contentWrapperRef.current?.offsetTop]) + + if (!getTxDataResult) { + return null + } + const { + txHashState, + parentTxReceipt, + l2ToL1MessagesToShow: _l2ToL1MessagesToShow, + allMessages + } = getTxDataResult + + if (typeof parentTxReceipt === 'undefined') { + return null + } + + const transactions = [{ ...parentTxReceipt, allMessages }] return ( - + /> */} ) }} diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages.ts new file mode 100644 index 0000000000..4bce17599e --- /dev/null +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages.ts @@ -0,0 +1,91 @@ +import { + getArbitrumNetwork, + getArbitrumNetworks, + ParentTransactionReceipt +} from '@arbitrum/sdk' + +import { + EthDepositMessageWithNetwork, + ParentToChildMessagesAndDepositMessages +} from './helpers' +import { getProviderForChainId } from '@/token-bridge-sdk/utils' + +export const getParentToChildMessagesAndDepositMessages = async ( + parentTxReceipt: ParentTransactionReceipt, + parentChainId: number +): Promise => { + const childNetworks = getArbitrumNetworks().map( + network => network.parentChainId + ) + const messagesPromises = childNetworks.map(async childChainId => { + // TODO: error handle + const childNetwork = await getArbitrumNetwork(childChainId) + + // Check if any l1ToL2 msg is sent to the inbox of this l2Network + const logFromL2Inbox = parentTxReceipt.logs.filter(log => { + return ( + log.address.toLowerCase() === childNetwork.ethBridge.inbox.toLowerCase() + ) + }) + if (logFromL2Inbox.length === 0) { + return + } + + const childProvider = getProviderForChainId(childChainId) + const isClassic = await parentTxReceipt.isClassic(childProvider) + + if (isClassic) { + const messages = ( + await parentTxReceipt.getParentToChildMessagesClassic(childProvider) + ).map(l1ToL2Message => { + return Object.assign(l1ToL2Message, { childNetwork }) + }) + return { + allL1ToL2Messages: [], + allL1ToL2MessagesClassic: messages, + allDepositMessages: [] + } + } else { + const messages = ( + await parentTxReceipt.getParentToChildMessages(childProvider) + ).map(l1ToL2Message => { + return Object.assign(l1ToL2Message, { childNetwork }) + }) + + const depositMessagesWithNetwork: EthDepositMessageWithNetwork[] = ( + await parentTxReceipt.getEthDeposits(childProvider) + ).map(depositMessage => { + return Object.assign(depositMessage, { childNetwork }) + }) + + return { + allL1ToL2Messages: messages, + allDepositMessages: depositMessagesWithNetwork, + allL1ToL2MessagesClassic: [] + } + } + }) + + const messages = await Promise.all(messagesPromises) + + const allMessages = messages.reduce( + (acc, value) => { + if (!value) { + return acc + } + return { + retryables: acc.retryables.concat(value.allL1ToL2Messages), + retryablesClassic: acc.retryablesClassic.concat( + value.allL1ToL2MessagesClassic + ), + deposits: acc.deposits.concat(value.allDepositMessages) + } + }, + { + retryables: [], + retryablesClassic: [], + deposits: [] + } as ParentToChildMessagesAndDepositMessages + ) + return allMessages +} diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts index 5615cb4719..2b3c774a24 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts @@ -5,8 +5,11 @@ import { ChildToParentMessageReader, ChildToParentMessageStatus, ChildTransactionReceipt, + EthDepositMessage, getArbitrumNetwork, - getArbitrumNetworks + getArbitrumNetworks, + ParentToChildMessageReader, + ParentToChildMessageReaderClassic } from '@arbitrum/sdk' import { getProviderForChainId } from '@/token-bridge-sdk/utils' @@ -31,6 +34,26 @@ export enum ReceiptState { NO_L2_L1_MESSAGES } +export interface ParentToChildMessageReaderWithNetwork + extends ParentToChildMessageReader { + childNetwork: ArbitrumNetwork +} + +export interface ParentToChildMessageReaderClassicWithNetwork + extends ParentToChildMessageReaderClassic { + childNetwork: ArbitrumNetwork +} + +export interface EthDepositMessageWithNetwork extends EthDepositMessage { + childNetwork: ArbitrumNetwork +} + +export interface ParentToChildMessagesAndDepositMessages { + retryables: ParentToChildMessageReaderWithNetwork[] + retryablesClassic: ParentToChildMessageReaderClassicWithNetwork[] + deposits: EthDepositMessageWithNetwork[] +} + export interface ChildToParentMessageData { status: ChildToParentMessageStatus childToParentMessage: ChildToParentMessage 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 6e982ebb46..e379b7c094 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts @@ -617,23 +617,30 @@ export const supportedParentChains = [ ...supportedCustomOrbitParentChains ] +export type GetParentTxReceiptResult = { + parentTxReceipt: ParentTransactionReceipt | undefined + parentChainId: number +} + export async function getParentTxReceipt( txHash: string -): Promise { - const promises = Object.entries(supportedParentChains).map( - async ([chainId]) => { - try { - const l1Provider = getProviderForChainId(Number(chainId)) - - const receipt = await l1Provider.getTransactionReceipt(txHash) - if (receipt) { - return new ParentTransactionReceipt(receipt) +): Promise { + console.log('supportedParentChains? ', supportedParentChains) + const promises = supportedParentChains.map(async chainId => { + try { + const l1Provider = getProviderForChainId(Number(chainId)) + + const receipt = await l1Provider.getTransactionReceipt(txHash) + if (receipt) { + return { + parentTxReceipt: new ParentTransactionReceipt(receipt), + parentChainId: Number(chainId) } - } catch (e) { - console.warn(`Cannot get tx receipt from parent chain ${chainId}`) } + } catch (e) { + console.warn(`Cannot get tx receipt from parent chain ${chainId}`) } - ) + }) const results = await Promise.all(promises) return results.find(r => r) } From e0e6d995f8cb94d6e091b40141f23e8bc5ccc3a2 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Fri, 29 Nov 2024 17:03:13 +0000 Subject: [PATCH 36/49] make search by tx id work --- .../TransactionHistoryTxHashSearchResult.tsx | 90 +++++++---- ...ParentToChildMessagesAndDepositMessages.ts | 142 ++++++++++-------- .../TransactionHistoryTxHashSearch/helpers.ts | 1 + .../components/TransactionHistory/helpers.ts | 1 - 4 files changed, 142 insertions(+), 92 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx index 2513f50214..ad576c4c32 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx @@ -1,11 +1,9 @@ import { Column, Table } from 'react-virtualized' import { useEffect, useMemo, useRef } from 'react' -import { twMerge } from 'tailwind-merge' import { ParentTransactionReceipt } from '@arbitrum/sdk' import useSWR from 'swr' import { ContentWrapper, TableHeader } from '../TransactionHistoryTable' -import { TransactionsTableRow } from '../TransactionsTableRow' import { getParentTxReceipt } from '../helpers' import { ChildToParentMessageData, @@ -16,30 +14,37 @@ import { } from './helpers' import { getParentToChildMessagesAndDepositMessages } from './getParentToChildMessagesAndDepositMessages' import { useTransactionHistoryAddressStore } from '../TransactionHistorySearchBar' +import { TransactionsTableRow } from '../TransactionsTableRow' +import { twMerge } from 'tailwind-merge' +import { MergedTransaction } from '../../../state/app/state' +import { AssetType } from '../../../hooks/arbTokenBridge.types' +import { formatAmount } from '../../../util/NumberUtils' async function getData(txHash: string | undefined) { - console.log('in getData: ', txHash) if (!txHash) { return } const getParentTxReceiptResult = await getParentTxReceipt(txHash) - const parentTxReceipt = getParentTxReceiptResult + const parentTxReceiptAndChainId = getParentTxReceiptResult const defaultReturn: { allMessages: ParentToChildMessagesAndDepositMessages l2ToL1MessagesToShow: ChildToParentMessageData[] parentTxReceipt: ParentTransactionReceipt | undefined + parentChainId: number | undefined } = { allMessages: { retryables: [], retryablesClassic: [], - deposits: [] + deposits: [], + childChainId: null }, l2ToL1MessagesToShow: [], - parentTxReceipt: getParentTxReceiptResult?.parentTxReceipt + parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, + parentChainId: parentTxReceiptAndChainId?.parentChainId } - if (getParentTxReceiptResult === undefined) { + if (typeof getParentTxReceiptResult === 'undefined') { const res = await getChildToParentMessages(txHash) const { childTxStatus, childToParentMessages } = res @@ -47,7 +52,8 @@ async function getData(txHash: string | undefined) { if (childToParentMessages.length > 0) { return { ...defaultReturn, - parentTxReceipt, + parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, + parentChainId: parentTxReceiptAndChainId?.parentChainId, txHashState: ReceiptState.MESSAGES_FOUND, l2ToL1MessagesToShow: childToParentMessages } @@ -55,21 +61,24 @@ async function getData(txHash: string | undefined) { if (childTxStatus === ChildTxStatus.SUCCESS) { return { ...defaultReturn, - parentTxReceipt, + parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, + parentChainId: parentTxReceiptAndChainId?.parentChainId, txHashState: ReceiptState.NO_L2_L1_MESSAGES } } if (childTxStatus === ChildTxStatus.FAILURE) { return { ...defaultReturn, - parentTxReceipt, + parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, + parentChainId: parentTxReceiptAndChainId?.parentChainId, txHashState: ReceiptState.L2_FAILED } } return { ...defaultReturn, - parentTxReceipt, + parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, + parentChainId: parentTxReceiptAndChainId?.parentChainId, txHashState: ReceiptState.NOT_FOUND } } @@ -82,15 +91,18 @@ async function getData(txHash: string | undefined) { ) { return { ...defaultReturn, - parentTxReceipt, + parentTxReceipt: _parentTxReceipt, txHashState: ReceiptState.L1_FAILED } } + console.log('getParentTxReceiptResult? ', getParentTxReceiptResult) + console.log('_parentTxReceipt? ', _parentTxReceipt) const allMessages = await getParentToChildMessagesAndDepositMessages( _parentTxReceipt, parentChainId ) + console.log('allMessages? ', allMessages) const l1ToL2Messages = allMessages.retryables const l1ToL2MessagesClassic = allMessages.retryablesClassic const depositMessages = allMessages.deposits @@ -101,7 +113,8 @@ async function getData(txHash: string | undefined) { ) { return { ...defaultReturn, - parentTxReceipt, + parentTxReceipt: _parentTxReceipt, + parentChainId, txHashState: ReceiptState.NO_L1_L2_MESSAGES } } @@ -109,7 +122,8 @@ async function getData(txHash: string | undefined) { return { ...defaultReturn, allMessages, - parentTxReceipt, + parentTxReceipt: _parentTxReceipt, + parentChainId, txHashState: ReceiptState.MESSAGES_FOUND } } @@ -128,12 +142,7 @@ export function TransactionHistoryTxHashSearchResult() { data: getTxDataResult, isLoading, error - } = useSWR(queryKey, ([_txHash]) => getData(_txHash), { - refreshInterval: 30_000, - shouldRetryOnError: true - }) - - console.log('getTxDataResult: ', getTxDataResult) + } = useSWR(queryKey, ([txHash]) => getData(txHash)) const TABLE_HEADER_HEIGHT = 52 const TABLE_ROW_HEIGHT = 60 @@ -160,6 +169,7 @@ export function TransactionHistoryTxHashSearchResult() { const { txHashState, parentTxReceipt, + parentChainId, l2ToL1MessagesToShow: _l2ToL1MessagesToShow, allMessages } = getTxDataResult @@ -168,7 +178,35 @@ export function TransactionHistoryTxHashSearchResult() { return null } - const transactions = [{ ...parentTxReceipt, allMessages }] + if (!parentChainId) { + return null + } + + const tx: MergedTransaction = { + sender: parentTxReceipt.from, + direction: 'deposit', + status: txHashState.toString(), // need mapping + blockNum: parentTxReceipt.blockNumber, + parentChainId, + sourceChainId: parentChainId, + isWithdrawal: false, + destination: parentTxReceipt.to, + value: formatAmount(allMessages.deposits[0]?.value) ?? null, + txId: parentTxReceipt.transactionHash, + createdAt: null, + resolvedAt: null, + asset: allMessages.deposits[0] ? 'ETH' : 'Some ERC20', + assetType: allMessages.deposits[0] ? AssetType.ETH : AssetType.ERC20, + uniqueId: null, + tokenAddress: + typeof allMessages.deposits[0] === 'undefined' ? '0xtoken' : null, + childChainId: allMessages.childChainId ?? 0, + destinationChainId: allMessages.childChainId ?? 0 + + // parentTxReceipt, allMessages + } + + const transactions = [tx] return ( - {/* */} + /> ) }} diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages.ts index 4bce17599e..6ceb9c1bc4 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages.ts @@ -14,78 +14,94 @@ export const getParentToChildMessagesAndDepositMessages = async ( parentTxReceipt: ParentTransactionReceipt, parentChainId: number ): Promise => { - const childNetworks = getArbitrumNetworks().map( - network => network.parentChainId - ) - const messagesPromises = childNetworks.map(async childChainId => { - // TODO: error handle - const childNetwork = await getArbitrumNetwork(childChainId) + try { + const childNetworks = getArbitrumNetworks().map(network => network.chainId) + const messagesPromises = childNetworks.map(async childChainId => { + // TODO: error handle + const childNetwork = await getArbitrumNetwork(childChainId) - // Check if any l1ToL2 msg is sent to the inbox of this l2Network - const logFromL2Inbox = parentTxReceipt.logs.filter(log => { - return ( - log.address.toLowerCase() === childNetwork.ethBridge.inbox.toLowerCase() - ) - }) - if (logFromL2Inbox.length === 0) { - return - } - - const childProvider = getProviderForChainId(childChainId) - const isClassic = await parentTxReceipt.isClassic(childProvider) - - if (isClassic) { - const messages = ( - await parentTxReceipt.getParentToChildMessagesClassic(childProvider) - ).map(l1ToL2Message => { - return Object.assign(l1ToL2Message, { childNetwork }) + // Check if any l1ToL2 msg is sent to the inbox of this l2Network + const logFromL2Inbox = parentTxReceipt.logs.filter(log => { + return ( + log.address.toLowerCase() === + childNetwork.ethBridge.inbox.toLowerCase() + ) }) - return { - allL1ToL2Messages: [], - allL1ToL2MessagesClassic: messages, - allDepositMessages: [] + if (logFromL2Inbox.length === 0) { + return } - } else { - const messages = ( - await parentTxReceipt.getParentToChildMessages(childProvider) - ).map(l1ToL2Message => { - return Object.assign(l1ToL2Message, { childNetwork }) - }) - const depositMessagesWithNetwork: EthDepositMessageWithNetwork[] = ( - await parentTxReceipt.getEthDeposits(childProvider) - ).map(depositMessage => { - return Object.assign(depositMessage, { childNetwork }) - }) + const childProvider = getProviderForChainId(childChainId) + const isClassic = await parentTxReceipt.isClassic(childProvider) - return { - allL1ToL2Messages: messages, - allDepositMessages: depositMessagesWithNetwork, - allL1ToL2MessagesClassic: [] - } - } - }) + if (isClassic) { + const messages = ( + await parentTxReceipt.getParentToChildMessagesClassic(childProvider) + ).map(l1ToL2Message => { + return Object.assign(l1ToL2Message, { childNetwork }) + }) + return { + allL1ToL2Messages: [], + allL1ToL2MessagesClassic: messages, + allDepositMessages: [], + childChainId + } + } else { + const messages = ( + await parentTxReceipt.getParentToChildMessages(childProvider) + ).map(l1ToL2Message => { + return Object.assign(l1ToL2Message, { childNetwork }) + }) - const messages = await Promise.all(messagesPromises) + const depositMessagesWithNetwork: EthDepositMessageWithNetwork[] = ( + await parentTxReceipt.getEthDeposits(childProvider) + ).map(depositMessage => { + return Object.assign(depositMessage, { childNetwork }) + }) - const allMessages = messages.reduce( - (acc, value) => { - if (!value) { - return acc - } - return { - retryables: acc.retryables.concat(value.allL1ToL2Messages), - retryablesClassic: acc.retryablesClassic.concat( - value.allL1ToL2MessagesClassic - ), - deposits: acc.deposits.concat(value.allDepositMessages) + return { + allL1ToL2Messages: messages, + allDepositMessages: depositMessagesWithNetwork, + allL1ToL2MessagesClassic: [], + childChainId + } } - }, - { + }) + + const messages = await Promise.all(messagesPromises) + + const allMessages = messages.reduce( + (acc, value) => { + if (!value) { + return acc + } + return { + retryables: acc.retryables.concat(value.allL1ToL2Messages), + retryablesClassic: acc.retryablesClassic.concat( + value.allL1ToL2MessagesClassic + ), + deposits: acc.deposits.concat(value.allDepositMessages), + childChainId: value.childChainId + } + }, + { + retryables: [], + retryablesClassic: [], + deposits: [], + childChainId: null + } as ParentToChildMessagesAndDepositMessages + ) + return allMessages + } catch (error) { + console.error( + 'Error in getParentToChildMessagesAndDepositMessages: ', + error + ) + return { retryables: [], retryablesClassic: [], - deposits: [] + deposits: [], + childChainId: null } as ParentToChildMessagesAndDepositMessages - ) - return allMessages + } } diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts index 2b3c774a24..a7fe0bdc87 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts @@ -52,6 +52,7 @@ export interface ParentToChildMessagesAndDepositMessages { retryables: ParentToChildMessageReaderWithNetwork[] retryablesClassic: ParentToChildMessageReaderClassicWithNetwork[] deposits: EthDepositMessageWithNetwork[] + childChainId: number | null } export interface ChildToParentMessageData { 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 e379b7c094..ef15410d5a 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts @@ -625,7 +625,6 @@ export type GetParentTxReceiptResult = { export async function getParentTxReceipt( txHash: string ): Promise { - console.log('supportedParentChains? ', supportedParentChains) const promises = supportedParentChains.map(async chainId => { try { const l1Provider = getProviderForChainId(Number(chainId)) From 46e518738787afe65d1651ccd109d844d88b641b Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Fri, 29 Nov 2024 17:07:08 +0000 Subject: [PATCH 37/49] hide address search results when tx id search is active --- .../components/TransactionHistory/TransactionHistory.tsx | 3 ++- .../TransactionHistoryAddressSearchResults.tsx | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) 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 14fdbde265..06f723ae31 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx @@ -3,6 +3,7 @@ import { create } from 'zustand' import { MergedTransaction } from '../../state/app/state' import { TransactionHistorySearchBar } from './TransactionHistorySearchBar' import { TransactionHistoryTxHashSearchResult } from './TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult' +import { TransactionHistoryAddressSearchResults } from './TransactionHistoryAddressSearchResults' type TxDetailsStore = { tx: MergedTransaction | null @@ -37,7 +38,7 @@ export const TransactionHistory = () => { - {/* */} + ) } diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryAddressSearchResults.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryAddressSearchResults.tsx index 241d4ef948..5bed111dc6 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryAddressSearchResults.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryAddressSearchResults.tsx @@ -48,6 +48,7 @@ const tabClasses = 'text-white px-3 mr-2 border-b-2 ui-selected:border-white ui-not-selected:border-transparent ui-not-selected:text-white/80 arb-hover' export function TransactionHistoryAddressSearchResults() { + const { sanitizedTxHash } = useTransactionHistoryAddressStore() const props = useTransactionHistoryUpdater() const { transactions } = props const { searchError } = useTransactionHistoryAddressStore() @@ -92,6 +93,11 @@ export function TransactionHistoryAddressSearchResults() { const settledTransactions = groupedTransactions.settled + if (typeof sanitizedTxHash !== 'undefined') { + // show search by tx id results instead + return null + } + if (searchError) { return ( From 94c5a381726dfb869ae7b47845643c0c913432fa Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Fri, 29 Nov 2024 17:13:55 +0000 Subject: [PATCH 38/49] clean up --- .../TransactionHistory/TransactionHistorySearchBar.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx index 3ad6eecfd1..7d303addfc 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx @@ -34,12 +34,18 @@ export const useTransactionHistoryAddressStore = } }, sanitizedTxHash: undefined, - setSanitizedTxHash: (txHash: string) => { + setSanitizedTxHash: (txHash: string | undefined) => { if (isValidTxHash(txHash)) { set({ sanitizedTxHash: txHash }) } + + if (typeof txHash === 'undefined') { + set({ + sanitizedTxHash: undefined + }) + } }, searchError: undefined, setSearchError: (error: TransactionHistorySearchError | undefined) => @@ -59,6 +65,7 @@ export function TransactionHistorySearchBar() { useEffect(() => { if (addressOrTxHash === '' && connectedAddress) { setSanitizedAddress(connectedAddress) + setSanitizedTxHash(undefined) setSearchError(undefined) } }, [addressOrTxHash, connectedAddress, setSanitizedAddress, setSearchError]) From 8483068fa59ee35c9cb355de2e4b199c13492c01 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Fri, 29 Nov 2024 18:16:50 +0000 Subject: [PATCH 39/49] fix type --- .../TransactionHistory/TransactionHistorySearchBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx index 7d303addfc..2320cabae8 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistorySearchBar.tsx @@ -19,7 +19,7 @@ type TransactionHistoryAddressStore = { searchError: TransactionHistorySearchError | undefined setAddressOrTxHash: (addressOrTxHash: string) => void setSanitizedAddress: (address: string) => void - setSanitizedTxHash: (txHash: string) => void + setSanitizedTxHash: (txHash: string | undefined) => void setSearchError: (error: TransactionHistorySearchError | undefined) => void } From 7b090df8b89a2b71140797dc2d12cc059cc7c612 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 10 Dec 2024 19:51:08 +0000 Subject: [PATCH 40/49] wip --- .../TransactionHistoryTxHashSearchResult.tsx | 178 +--------------- .../src/pages/api/deposits.ts | 2 +- .../deposits/fetchDepositTxFromSubgraph.ts | 195 ++++++++++++++++++ .../src/util/deposits/fetchDeposits.ts | 57 +---- .../deposits/fetchDepositsFromSubgraph.ts | 7 +- .../util/deposits/mapDepositsFromSubgraph.ts | 66 ++++++ 6 files changed, 285 insertions(+), 220 deletions(-) create mode 100644 packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts create mode 100644 packages/arb-token-bridge-ui/src/util/deposits/mapDepositsFromSubgraph.ts diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx index ad576c4c32..4c5e5d088a 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx @@ -1,132 +1,15 @@ import { Column, Table } from 'react-virtualized' -import { useEffect, useMemo, useRef } from 'react' -import { ParentTransactionReceipt } from '@arbitrum/sdk' +import { useMemo, useRef } from 'react' import useSWR from 'swr' import { ContentWrapper, TableHeader } from '../TransactionHistoryTable' -import { getParentTxReceipt } from '../helpers' -import { - ChildToParentMessageData, - ChildTxStatus, - getChildToParentMessages, - ParentToChildMessagesAndDepositMessages, - ReceiptState -} from './helpers' -import { getParentToChildMessagesAndDepositMessages } from './getParentToChildMessagesAndDepositMessages' import { useTransactionHistoryAddressStore } from '../TransactionHistorySearchBar' import { TransactionsTableRow } from '../TransactionsTableRow' import { twMerge } from 'tailwind-merge' import { MergedTransaction } from '../../../state/app/state' import { AssetType } from '../../../hooks/arbTokenBridge.types' import { formatAmount } from '../../../util/NumberUtils' - -async function getData(txHash: string | undefined) { - if (!txHash) { - return - } - - const getParentTxReceiptResult = await getParentTxReceipt(txHash) - const parentTxReceiptAndChainId = getParentTxReceiptResult - const defaultReturn: { - allMessages: ParentToChildMessagesAndDepositMessages - l2ToL1MessagesToShow: ChildToParentMessageData[] - parentTxReceipt: ParentTransactionReceipt | undefined - parentChainId: number | undefined - } = { - allMessages: { - retryables: [], - retryablesClassic: [], - deposits: [], - childChainId: null - }, - l2ToL1MessagesToShow: [], - parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, - parentChainId: parentTxReceiptAndChainId?.parentChainId - } - - if (typeof getParentTxReceiptResult === 'undefined') { - const res = await getChildToParentMessages(txHash) - const { childTxStatus, childToParentMessages } = res - - // TODO: handle terminal states - if (childToParentMessages.length > 0) { - return { - ...defaultReturn, - parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, - parentChainId: parentTxReceiptAndChainId?.parentChainId, - txHashState: ReceiptState.MESSAGES_FOUND, - l2ToL1MessagesToShow: childToParentMessages - } - } - if (childTxStatus === ChildTxStatus.SUCCESS) { - return { - ...defaultReturn, - parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, - parentChainId: parentTxReceiptAndChainId?.parentChainId, - txHashState: ReceiptState.NO_L2_L1_MESSAGES - } - } - if (childTxStatus === ChildTxStatus.FAILURE) { - return { - ...defaultReturn, - parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, - parentChainId: parentTxReceiptAndChainId?.parentChainId, - txHashState: ReceiptState.L2_FAILED - } - } - - return { - ...defaultReturn, - parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, - parentChainId: parentTxReceiptAndChainId?.parentChainId, - txHashState: ReceiptState.NOT_FOUND - } - } - - const { parentTxReceipt: _parentTxReceipt, parentChainId } = - getParentTxReceiptResult - if ( - _parentTxReceipt?.status === 0 || - typeof _parentTxReceipt === 'undefined' - ) { - return { - ...defaultReturn, - parentTxReceipt: _parentTxReceipt, - txHashState: ReceiptState.L1_FAILED - } - } - console.log('getParentTxReceiptResult? ', getParentTxReceiptResult) - console.log('_parentTxReceipt? ', _parentTxReceipt) - - const allMessages = await getParentToChildMessagesAndDepositMessages( - _parentTxReceipt, - parentChainId - ) - console.log('allMessages? ', allMessages) - const l1ToL2Messages = allMessages.retryables - const l1ToL2MessagesClassic = allMessages.retryablesClassic - const depositMessages = allMessages.deposits - if ( - l1ToL2Messages.length === 0 && - l1ToL2MessagesClassic.length === 0 && - depositMessages.length === 0 - ) { - return { - ...defaultReturn, - parentTxReceipt: _parentTxReceipt, - parentChainId, - txHashState: ReceiptState.NO_L1_L2_MESSAGES - } - } - - return { - ...defaultReturn, - allMessages, - parentTxReceipt: _parentTxReceipt, - parentChainId, - txHashState: ReceiptState.MESSAGES_FOUND - } -} +import { fetchDepositTxFromSubgraph } from '../../../util/deposits/fetchDepositTxFromSubgraph' export function TransactionHistoryTxHashSearchResult() { const contentWrapperRef = useRef(null) @@ -139,10 +22,10 @@ export function TransactionHistoryTxHashSearchResult() { return [sanitizedTxHash, 'TransactionHistoryTxHashSearchResult'] as const }, [sanitizedTxHash]) const { - data: getTxDataResult, + data: transactions, isLoading, error - } = useSWR(queryKey, ([txHash]) => getData(txHash)) + } = useSWR(queryKey, ([txHash]) => fetchDepositTxFromSubgraph(txHash)) const TABLE_HEADER_HEIGHT = 52 const TABLE_ROW_HEIGHT = 60 @@ -163,50 +46,9 @@ export function TransactionHistoryTxHashSearchResult() { ) }, [contentWrapperRef.current?.offsetTop]) - if (!getTxDataResult) { + if (!transactions) { return null } - const { - txHashState, - parentTxReceipt, - parentChainId, - l2ToL1MessagesToShow: _l2ToL1MessagesToShow, - allMessages - } = getTxDataResult - - if (typeof parentTxReceipt === 'undefined') { - return null - } - - if (!parentChainId) { - return null - } - - const tx: MergedTransaction = { - sender: parentTxReceipt.from, - direction: 'deposit', - status: txHashState.toString(), // need mapping - blockNum: parentTxReceipt.blockNumber, - parentChainId, - sourceChainId: parentChainId, - isWithdrawal: false, - destination: parentTxReceipt.to, - value: formatAmount(allMessages.deposits[0]?.value) ?? null, - txId: parentTxReceipt.transactionHash, - createdAt: null, - resolvedAt: null, - asset: allMessages.deposits[0] ? 'ETH' : 'Some ERC20', - assetType: allMessages.deposits[0] ? AssetType.ETH : AssetType.ERC20, - uniqueId: null, - tokenAddress: - typeof allMessages.deposits[0] === 'undefined' ? '0xtoken' : null, - childChainId: allMessages.childChainId ?? 0, - destinationChainId: allMessages.childChainId ?? 0 - - // parentTxReceipt, allMessages - } - - const transactions = [tx] return ( { const tx = transactions[index] + console.log('tx????', tx) + if (!tx) { - return + return null } const isLastRow = index + 1 === transactions.length - const key = `${tx.parentChainId}-${tx.childChainId}-${tx.txId}` - - console.log('tx: ', tx) + const key = `${tx.parentChainId}-${tx.childChainId}-${tx.txID}` return (
diff --git a/packages/arb-token-bridge-ui/src/pages/api/deposits.ts b/packages/arb-token-bridge-ui/src/pages/api/deposits.ts index 0250082bde..6efea35578 100644 --- a/packages/arb-token-bridge-ui/src/pages/api/deposits.ts +++ b/packages/arb-token-bridge-ui/src/pages/api/deposits.ts @@ -54,7 +54,7 @@ export default async function handler( // validate the request parameters const errorMessage = [] if (!l2ChainId) errorMessage.push(' is required') - if (!sender && !receiver) + if (search.length === 0 && !sender && !receiver) errorMessage.push(' or is required') if (errorMessage.length) { diff --git a/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts b/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts new file mode 100644 index 0000000000..f75cd47011 --- /dev/null +++ b/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts @@ -0,0 +1,195 @@ +import { getArbitrumNetworks } from '@arbitrum/sdk' + +import { FetchDepositsFromSubgraphResult } from './fetchDepositsFromSubgraph' +import { getAPIBaseUrl, sanitizeQueryParams } from '..' +import { hasL1Subgraph } from '../SubgraphUtils' +import { getNetworkName } from '../networks' +import { Transaction } from '../../hooks/useTransactions' +import { fetchNativeCurrency } from '../../hooks/useNativeCurrency' +import { mapDepositsFromSubgraph } from './mapDepositsFromSubgraph' + +import { getProviderForChainId } from '@/token-bridge-sdk/utils' + +/** + * Fetches initiated ETH deposit from event logs using transaction id + * + * @param txHash transaction id on parent chain + */ +export async function fetchDepositTxFromSubgraph( + txHash: string +): Promise { + const supportedChildChains = getArbitrumNetworks() + + const fetcherList = supportedChildChains.map(childChain => { + const childChainId = childChain.chainId + + if (!hasL1Subgraph(Number(childChainId))) { + console.error( + `Parent chain subgraph not available for network: ${getNetworkName( + childChainId + )} (${childChainId})` + ) + return undefined + } + + const urlParams = new URLSearchParams( + sanitizeQueryParams({ + search: txHash, + l2ChainId: childChainId + }) + ) + + return { + fetcher: fetch(`${getAPIBaseUrl()}/api/deposits?${urlParams}`, { + method: 'GET', + headers: { 'Content-Type': 'application/json' } + }), + parentChainId: childChain.parentChainId, + childChainId + } + }) + + try { + const responses = await Promise.all(fetcherList.map(item => item?.fetcher)) + + // there is always only one response because this tx only lives on one chain and no collision is possible + const result = responses + .map((response, index) => { + const fetcherItem = fetcherList[index] + if (typeof fetcherItem === 'undefined') { + return undefined + } + return { + response, + parentChainId: fetcherItem.parentChainId, + childChainId: fetcherItem.childChainId + } + }) + .filter(Boolean)[0] as { + response: Response + parentChainId: number + childChainId: number + } + + const depositsFromSubgraph: FetchDepositsFromSubgraphResult[] = ( + await result.response.json() + ).data + + const nativeCurrency = await fetchNativeCurrency({ + provider: getProviderForChainId(result.childChainId) + }) + + const mappedDepositsFromSubgraph: Transaction[] = mapDepositsFromSubgraph({ + depositsFromSubgraph, + nativeCurrency, + l1ChainId: result.parentChainId, + l2ChainId: result.childChainId + }) + + return mappedDepositsFromSubgraph + } catch (error) { + return undefined + } + + // const getParentTxReceiptResult = await getParentTxReceipt(txHash) + // const parentTxReceiptAndChainId = getParentTxReceiptResult + // const defaultReturn: { + // allMessages: ParentToChildMessagesAndDepositMessages + // l2ToL1MessagesToShow: ChildToParentMessageData[] + // parentTxReceipt: ParentTransactionReceipt | undefined + // parentChainId: number | undefined + // } = { + // allMessages: { + // retryables: [], + // retryablesClassic: [], + // deposits: [], + // childChainId: null + // }, + // l2ToL1MessagesToShow: [], + // parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, + // parentChainId: parentTxReceiptAndChainId?.parentChainId + // } + + // if (typeof getParentTxReceiptResult === 'undefined') { + // const res = await getChildToParentMessages(txHash) + // const { childTxStatus, childToParentMessages } = res + + // // TODO: handle terminal states + // if (childToParentMessages.length > 0) { + // return { + // ...defaultReturn, + // parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, + // parentChainId: parentTxReceiptAndChainId?.parentChainId, + // txHashState: ReceiptState.MESSAGES_FOUND, + // l2ToL1MessagesToShow: childToParentMessages + // } + // } + // if (childTxStatus === ChildTxStatus.SUCCESS) { + // return { + // ...defaultReturn, + // parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, + // parentChainId: parentTxReceiptAndChainId?.parentChainId, + // txHashState: ReceiptState.NO_L2_L1_MESSAGES + // } + // } + // if (childTxStatus === ChildTxStatus.FAILURE) { + // return { + // ...defaultReturn, + // parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, + // parentChainId: parentTxReceiptAndChainId?.parentChainId, + // txHashState: ReceiptState.L2_FAILED + // } + // } + + // return { + // ...defaultReturn, + // parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, + // parentChainId: parentTxReceiptAndChainId?.parentChainId, + // txHashState: ReceiptState.NOT_FOUND + // } + // } + + // const { parentTxReceipt: _parentTxReceipt, parentChainId } = + // getParentTxReceiptResult + // if ( + // _parentTxReceipt?.status === 0 || + // typeof _parentTxReceipt === 'undefined' + // ) { + // return { + // ...defaultReturn, + // parentTxReceipt: _parentTxReceipt, + // txHashState: ReceiptState.L1_FAILED + // } + // } + // console.log('getParentTxReceiptResult? ', getParentTxReceiptResult) + // console.log('_parentTxReceipt? ', _parentTxReceipt) + + // const allMessages = await getParentToChildMessagesAndDepositMessages( + // _parentTxReceipt, + // parentChainId + // ) + // console.log('allMessages? ', allMessages) + // const l1ToL2Messages = allMessages.retryables + // const l1ToL2MessagesClassic = allMessages.retryablesClassic + // const depositMessages = allMessages.deposits + // if ( + // l1ToL2Messages.length === 0 && + // l1ToL2MessagesClassic.length === 0 && + // depositMessages.length === 0 + // ) { + // return { + // ...defaultReturn, + // parentTxReceipt: _parentTxReceipt, + // parentChainId, + // txHashState: ReceiptState.NO_L1_L2_MESSAGES + // } + // } + + // return { + // ...defaultReturn, + // allMessages, + // parentTxReceipt: _parentTxReceipt, + // parentChainId, + // txHashState: ReceiptState.MESSAGES_FOUND + // } +} diff --git a/packages/arb-token-bridge-ui/src/util/deposits/fetchDeposits.ts b/packages/arb-token-bridge-ui/src/util/deposits/fetchDeposits.ts index b2ec571855..887b950ea8 100644 --- a/packages/arb-token-bridge-ui/src/util/deposits/fetchDeposits.ts +++ b/packages/arb-token-bridge-ui/src/util/deposits/fetchDeposits.ts @@ -7,12 +7,12 @@ import { } from './fetchDepositsFromSubgraph' import { AssetType } from '../../hooks/arbTokenBridge.types' import { Transaction } from '../../hooks/useTransactions' -import { defaultErc20Decimals } from '../../defaults' import { fetchNativeCurrency } from '../../hooks/useNativeCurrency' import { fetchEthDepositsToCustomDestinationFromSubgraph, FetchEthDepositsToCustomDestinationFromSubgraphResult } from './fetchEthDepositsToCustomDestinationFromSubgraph' +import { mapDepositsFromSubgraph } from './mapDepositsFromSubgraph' export type FetchDepositParams = { sender?: string @@ -84,55 +84,12 @@ export const fetchDeposits = async ({ ) } - const mappedDepositsFromSubgraph: Transaction[] = depositsFromSubgraph.map( - (tx: FetchDepositsFromSubgraphResult) => { - const isEthDeposit = tx.type === 'EthDeposit' - - const assetDetails = { - assetName: nativeCurrency.symbol, - assetType: AssetType.ETH, - tokenAddress: '' - } - - if (!isEthDeposit) { - // update some values for token deposit - const symbol = tx.l1Token?.symbol || '' - - assetDetails.assetName = symbol - assetDetails.assetType = AssetType.ERC20 - assetDetails.tokenAddress = tx?.l1Token?.id || '' - } - - const amount = isEthDeposit ? tx.ethValue : tx.tokenAmount - - const tokenDecimals = tx?.l1Token?.decimals ?? defaultErc20Decimals - const decimals = isEthDeposit ? nativeCurrency.decimals : tokenDecimals - - return { - type: 'deposit-l1', - status: 'pending', - direction: 'deposit', - source: 'subgraph', - value: utils.formatUnits(amount || 0, decimals), - txID: tx.transactionHash, - tokenAddress: assetDetails.tokenAddress, - sender: tx.sender, - destination: tx.receiver, - - assetName: assetDetails.assetName, - assetType: assetDetails.assetType, - - l1NetworkID: String(l1ChainId), - l2NetworkID: String(l2ChainId), - blockNumber: Number(tx.blockCreatedAt), - timestampCreated: tx.timestamp, - isClassic: tx.isClassic, - - childChainId: l2ChainId, - parentChainId: l1ChainId - } - } - ) + const mappedDepositsFromSubgraph: Transaction[] = mapDepositsFromSubgraph({ + depositsFromSubgraph, + nativeCurrency, + l1ChainId, + l2ChainId + }) const mappedEthDepositsToCustomDestinationFromSubgraph: Transaction[] = ethDepositsToCustomDestinationFromSubgraph.map( diff --git a/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositsFromSubgraph.ts b/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositsFromSubgraph.ts index 730a471397..7fa91d14b3 100644 --- a/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositsFromSubgraph.ts +++ b/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositsFromSubgraph.ts @@ -1,3 +1,4 @@ +import { getNetworkName } from '../networks' import { hasL1Subgraph } from '../SubgraphUtils' import { getAPIBaseUrl, sanitizeQueryParams } from './../index' @@ -74,7 +75,11 @@ export const fetchDepositsFromSubgraph = async ({ ) if (!hasL1Subgraph(Number(l2ChainId))) { - throw new Error(`L1 subgraph not available for network: ${l2ChainId}`) + throw new Error( + `L1 subgraph not available for network: ${getNetworkName( + l2ChainId + )} (${l2ChainId})` + ) } if (pageSize === 0) return [] // don't query subgraph if nothing requested diff --git a/packages/arb-token-bridge-ui/src/util/deposits/mapDepositsFromSubgraph.ts b/packages/arb-token-bridge-ui/src/util/deposits/mapDepositsFromSubgraph.ts new file mode 100644 index 0000000000..45b0b531a0 --- /dev/null +++ b/packages/arb-token-bridge-ui/src/util/deposits/mapDepositsFromSubgraph.ts @@ -0,0 +1,66 @@ +import { utils } from 'ethers' +import { defaultErc20Decimals } from '../../defaults' +import { AssetType } from '../../hooks/arbTokenBridge.types' +import { NativeCurrency } from '../../hooks/useNativeCurrency' +import { FetchDepositsFromSubgraphResult } from './fetchDepositsFromSubgraph' +import { Transaction } from '../../hooks/useTransactions' + +export function mapDepositsFromSubgraph({ + depositsFromSubgraph, + nativeCurrency, + l1ChainId, + l2ChainId +}: { + depositsFromSubgraph: FetchDepositsFromSubgraphResult[] + nativeCurrency: NativeCurrency + l1ChainId: number + l2ChainId: number +}): Transaction[] { + return depositsFromSubgraph.map(tx => { + const isEthDeposit = tx.type === 'EthDeposit' + + const assetDetails = { + assetName: nativeCurrency.symbol, + assetType: AssetType.ETH, + tokenAddress: '' + } + + if (!isEthDeposit) { + // update some values for token deposit + const symbol = tx.l1Token?.symbol || '' + + assetDetails.assetName = symbol + assetDetails.assetType = AssetType.ERC20 + assetDetails.tokenAddress = tx?.l1Token?.id || '' + } + + const amount = isEthDeposit ? tx.ethValue : tx.tokenAmount + + const tokenDecimals = tx?.l1Token?.decimals ?? defaultErc20Decimals + const decimals = isEthDeposit ? nativeCurrency.decimals : tokenDecimals + + return { + type: 'deposit-l1', + status: 'pending', + direction: 'deposit', + source: 'subgraph', + value: utils.formatUnits(amount || 0, decimals), + txID: tx.transactionHash, + tokenAddress: assetDetails.tokenAddress, + sender: tx.sender, + destination: tx.receiver, + + assetName: assetDetails.assetName, + assetType: assetDetails.assetType, + + l1NetworkID: String(l1ChainId), + l2NetworkID: String(l2ChainId), + blockNumber: Number(tx.blockCreatedAt), + timestampCreated: tx.timestamp, + isClassic: tx.isClassic, + + childChainId: l2ChainId, + parentChainId: l1ChainId + } + }) +} From d088385327f26db0ea388030defa6a023452503a Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 10 Dec 2024 21:16:39 +0000 Subject: [PATCH 41/49] wip --- .../TransactionHistoryTxHashSearchResult.tsx | 60 ++++++++++++++----- .../src/pages/api/deposits.ts | 2 +- .../deposits/fetchDepositTxFromSubgraph.ts | 49 +++++++++++---- 3 files changed, 83 insertions(+), 28 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx index 4c5e5d088a..1294efd683 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx @@ -1,31 +1,40 @@ import { Column, Table } from 'react-virtualized' -import { useMemo, useRef } from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' +import { twMerge } from 'tailwind-merge' import useSWR from 'swr' import { ContentWrapper, TableHeader } from '../TransactionHistoryTable' import { useTransactionHistoryAddressStore } from '../TransactionHistorySearchBar' import { TransactionsTableRow } from '../TransactionsTableRow' -import { twMerge } from 'tailwind-merge' import { MergedTransaction } from '../../../state/app/state' -import { AssetType } from '../../../hooks/arbTokenBridge.types' -import { formatAmount } from '../../../util/NumberUtils' import { fetchDepositTxFromSubgraph } from '../../../util/deposits/fetchDepositTxFromSubgraph' +import { useTransactionHistory } from '../../../hooks/useTransactionHistory' +import { getUpdatedEthDeposit, isTxPending } from '../helpers' export function TransactionHistoryTxHashSearchResult() { const contentWrapperRef = useRef(null) const tableRef = useRef
(null) - const { sanitizedTxHash } = useTransactionHistoryAddressStore() + const { sanitizedAddress, sanitizedTxHash } = + useTransactionHistoryAddressStore() + const [tx, setTx] = useState() + const queryKey = useMemo(() => { - if (!sanitizedTxHash) { + if (!sanitizedTxHash || !sanitizedAddress) { return null } - return [sanitizedTxHash, 'TransactionHistoryTxHashSearchResult'] as const - }, [sanitizedTxHash]) + return [ + sanitizedTxHash, + sanitizedAddress, + 'TransactionHistoryTxHashSearchResult' + ] as const + }, [sanitizedTxHash, sanitizedAddress]) const { data: transactions, isLoading, error - } = useSWR(queryKey, ([txHash]) => fetchDepositTxFromSubgraph(txHash)) + } = useSWR(queryKey, ([txHash, connectedAddress]) => + fetchDepositTxFromSubgraph(txHash, connectedAddress) + ) const TABLE_HEADER_HEIGHT = 52 const TABLE_ROW_HEIGHT = 60 @@ -46,6 +55,29 @@ export function TransactionHistoryTxHashSearchResult() { ) }, [contentWrapperRef.current?.offsetTop]) + const txFromHash = + typeof transactions === 'undefined' + ? null + : (transactions[0] as unknown as MergedTransaction) + + console.log('txFromHash: ', txFromHash) + + useEffect(() => { + async function updateTx() { + if (!txFromHash) { + return + } + const updatedTx = await getUpdatedEthDeposit(txFromHash) + if (updatedTx) { + setTx(updatedTx) + + console.log('updatedTx: ', updatedTx) + } + } + + updateTx() + }, [tx]) + if (!transactions) { return null } @@ -70,21 +102,19 @@ export function TransactionHistoryTxHashSearchResult() { className="table-auto last:border-b-0" rowGetter={({ index }) => transactions[index]} rowRenderer={({ index, style }) => { - const tx = transactions[index] - - console.log('tx????', tx) - if (!tx) { return null } + console.log('tx here!', tx) + const isLastRow = index + 1 === transactions.length - const key = `${tx.parentChainId}-${tx.childChainId}-${tx.txID}` + const key = `${tx.parentChainId}-${tx.childChainId}-${tx.txId}` return (
diff --git a/packages/arb-token-bridge-ui/src/pages/api/deposits.ts b/packages/arb-token-bridge-ui/src/pages/api/deposits.ts index 6efea35578..0250082bde 100644 --- a/packages/arb-token-bridge-ui/src/pages/api/deposits.ts +++ b/packages/arb-token-bridge-ui/src/pages/api/deposits.ts @@ -54,7 +54,7 @@ export default async function handler( // validate the request parameters const errorMessage = [] if (!l2ChainId) errorMessage.push(' is required') - if (search.length === 0 && !sender && !receiver) + if (!sender && !receiver) errorMessage.push(' or is required') if (errorMessage.length) { diff --git a/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts b/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts index f75cd47011..b689e04a1f 100644 --- a/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts +++ b/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts @@ -1,12 +1,13 @@ import { getArbitrumNetworks } from '@arbitrum/sdk' +import { Address } from 'wagmi' import { FetchDepositsFromSubgraphResult } from './fetchDepositsFromSubgraph' import { getAPIBaseUrl, sanitizeQueryParams } from '..' import { hasL1Subgraph } from '../SubgraphUtils' import { getNetworkName } from '../networks' -import { Transaction } from '../../hooks/useTransactions' import { fetchNativeCurrency } from '../../hooks/useNativeCurrency' import { mapDepositsFromSubgraph } from './mapDepositsFromSubgraph' +import { Transaction } from '../../types/Transactions' import { getProviderForChainId } from '@/token-bridge-sdk/utils' @@ -16,7 +17,8 @@ import { getProviderForChainId } from '@/token-bridge-sdk/utils' * @param txHash transaction id on parent chain */ export async function fetchDepositTxFromSubgraph( - txHash: string + txHash: string, + connectedAddress: Address ): Promise { const supportedChildChains = getArbitrumNetworks() @@ -35,7 +37,8 @@ export async function fetchDepositTxFromSubgraph( const urlParams = new URLSearchParams( sanitizeQueryParams({ search: txHash, - l2ChainId: childChainId + l2ChainId: childChainId, + sender: connectedAddress }) ) @@ -53,7 +56,7 @@ export async function fetchDepositTxFromSubgraph( const responses = await Promise.all(fetcherList.map(item => item?.fetcher)) // there is always only one response because this tx only lives on one chain and no collision is possible - const result = responses + const results = responses .map((response, index) => { const fetcherItem = fetcherList[index] if (typeof fetcherItem === 'undefined') { @@ -65,25 +68,47 @@ export async function fetchDepositTxFromSubgraph( childChainId: fetcherItem.childChainId } }) - .filter(Boolean)[0] as { + .filter(Boolean) as { response: Response parentChainId: number childChainId: number + }[] + + type DepositFromSubgraph = { + data: FetchDepositsFromSubgraphResult[] + parentChainId: number + childChainId: number } - const depositsFromSubgraph: FetchDepositsFromSubgraphResult[] = ( - await result.response.json() - ).data + const depositsFromSubgraph: DepositFromSubgraph[] = ( + await Promise.all(results.map(result => result.response.json())) + ) + .map((result, index) => ({ + data: result.data, + parentChainId: results[index]?.parentChainId, + childChainId: results[index]?.childChainId + })) + .filter( + (result): result is DepositFromSubgraph => + result.data.length > 0 && + typeof result.parentChainId !== 'undefined' && + typeof result.childChainId !== 'undefined' + ) + console.log('depositsFromSubgraph? ', depositsFromSubgraph) + + if (typeof depositsFromSubgraph[0] === 'undefined') { + return + } const nativeCurrency = await fetchNativeCurrency({ - provider: getProviderForChainId(result.childChainId) + provider: getProviderForChainId(depositsFromSubgraph[0].childChainId) }) const mappedDepositsFromSubgraph: Transaction[] = mapDepositsFromSubgraph({ - depositsFromSubgraph, + depositsFromSubgraph: depositsFromSubgraph[0].data, nativeCurrency, - l1ChainId: result.parentChainId, - l2ChainId: result.childChainId + l1ChainId: depositsFromSubgraph[0].parentChainId, + l2ChainId: depositsFromSubgraph[0].childChainId }) return mappedDepositsFromSubgraph From f39e8e97704ea7ed69859cfe802fbc9b55ded663 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:40:21 +0000 Subject: [PATCH 42/49] update --- .../getParentToChildEventsByTxHash.ts | 115 +++++++++++++++++ .../components/TransactionHistory/helpers.ts | 33 ++--- .../deposits/fetchDepositTxFromSubgraph.ts | 102 --------------- .../deposits/fetchEthDepositsFromEventLogs.ts | 121 ++++++++++++++++++ 4 files changed, 247 insertions(+), 124 deletions(-) create mode 100644 packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts create mode 100644 packages/arb-token-bridge-ui/src/util/deposits/fetchEthDepositsFromEventLogs.ts diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts new file mode 100644 index 0000000000..36335f9ecf --- /dev/null +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts @@ -0,0 +1,115 @@ +import { BigNumber } from 'ethers' +import { BlockTag } from '@ethersproject/providers' + +import { getParentTxReceipt } from '../helpers' + +/** + * Get event logs for ParentToChild transactions. + * Only support by tx hash for now. + * @param childProvider + * @param filter Block range filter + * @param position The batchnumber indexed field was removed in nitro and a position indexed field was added. + * For pre-nitro events the value passed in here will be used to find events with the same batchnumber. + * For post nitro events it will be used to find events with the same position. + * @param destination The parent destination of the ChildToParent message + * @param hash The uniqueId indexed field was removed in nitro and a hash indexed field was added. + * For pre-nitro events the value passed in here will be used to find events with the same uniqueId. + * For post nitro events it will be used to find events with the same hash. + * @param indexInBatch The index in the batch, only valid for pre-nitro events. This parameter is ignored post-nitro + * @returns Any classic and nitro events that match the provided filters. + */ +export async function getParentToChildEventsByTxHash( + childChainId: number, + filter: { fromBlock: BlockTag; toBlock: BlockTag }, + position?: BigNumber, + destination?: string, + txHash?: string, + indexInBatch?: BigNumber +): Promise<(ParentToChildTransactionEvent & { transactionHash: string })[]> { + if (!txHash) { + return [] + } + const getParentTxReceiptResult = await getParentTxReceipt( + txHash, + childChainId + ) + + const childChain = await getArbitrumNetwork(childProvider) + const childNitroGenesisBlock = getNitroGenesisBlock(childChain) + + const inClassicRange = (blockTag: BlockTag, nitroGenBlock: number) => { + if (typeof blockTag === 'string') { + // taking classic of "earliest", "latest", "earliest" and the nitro gen block + // yields 0, nitro gen, nitro gen since the classic range is always between 0 and nitro gen + + switch (blockTag) { + case 'earliest': + return 0 + case 'latest': + return nitroGenBlock + case 'pending': + return nitroGenBlock + default: + throw new Error(`Unrecognised block tag. ${blockTag}`) + } + } + return Math.min(blockTag, nitroGenBlock) + } + + const inNitroRange = (blockTag: BlockTag, nitroGenBlock: number) => { + // taking nitro range of "earliest", "latest", "earliest" and the nitro gen block + // yields nitro gen, latest, pending since the nitro range is always between nitro gen and latest/pending + + if (typeof blockTag === 'string') { + switch (blockTag) { + case 'earliest': + return nitroGenBlock + case 'latest': + return 'latest' + case 'pending': + return 'pending' + default: + throw new Error(`Unrecognised block tag. ${blockTag}`) + } + } + + return Math.max(blockTag, nitroGenBlock) + } + + // only fetch nitro events after the genesis block + const classicFilter = { + fromBlock: inClassicRange(filter.fromBlock, childNitroGenesisBlock), + toBlock: inClassicRange(filter.toBlock, childNitroGenesisBlock) + } + const logQueries = [] + if (classicFilter.fromBlock !== classicFilter.toBlock) { + logQueries.push( + classic.ChildToParentMessageClassic.getChildToParentEvents( + childProvider, + classicFilter, + position, + destination, + hash, + indexInBatch + ) + ) + } + + const nitroFilter = { + fromBlock: inNitroRange(filter.fromBlock, childNitroGenesisBlock), + toBlock: inNitroRange(filter.toBlock, childNitroGenesisBlock) + } + if (nitroFilter.fromBlock !== nitroFilter.toBlock) { + logQueries.push( + nitro.ChildToParentMessageNitro.getChildToParentEvents( + childProvider, + nitroFilter, + position, + destination, + hash + ) + ) + } + + return (await Promise.all(logQueries)).flat(1) +} 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 bf9e38d042..8e4069dfb4 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/helpers.ts @@ -617,31 +617,20 @@ export const supportedParentChains = [ ...supportedCustomOrbitParentChains ] -export type GetParentTxReceiptResult = { - parentTxReceipt: ParentTransactionReceipt | undefined +export async function getParentTxReceipt( + txHash: string, parentChainId: number -} +): Promise { + try { + const parentProvider = getProviderForChainId(Number(parentChainId)) -export async function getParentTxReceipt( - txHash: string -): Promise { - const promises = supportedParentChains.map(async chainId => { - try { - const l1Provider = getProviderForChainId(Number(chainId)) - - const receipt = await l1Provider.getTransactionReceipt(txHash) - if (receipt) { - return { - parentTxReceipt: new ParentTransactionReceipt(receipt), - parentChainId: Number(chainId) - } - } - } catch (e) { - console.warn(`Cannot get tx receipt from parent chain ${chainId}`) + const receipt = await parentProvider.getTransactionReceipt(txHash) + if (receipt) { + return new ParentTransactionReceipt(receipt) } - }) - const results = await Promise.all(promises) - return results.find(r => r) + } catch (e) { + console.warn(`Cannot get tx receipt from parent chain ${parentChainId}`) + } } export function isValidTxHash(txHash: string | undefined): txHash is string { diff --git a/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts b/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts index b689e04a1f..bd38a37537 100644 --- a/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts +++ b/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts @@ -115,106 +115,4 @@ export async function fetchDepositTxFromSubgraph( } catch (error) { return undefined } - - // const getParentTxReceiptResult = await getParentTxReceipt(txHash) - // const parentTxReceiptAndChainId = getParentTxReceiptResult - // const defaultReturn: { - // allMessages: ParentToChildMessagesAndDepositMessages - // l2ToL1MessagesToShow: ChildToParentMessageData[] - // parentTxReceipt: ParentTransactionReceipt | undefined - // parentChainId: number | undefined - // } = { - // allMessages: { - // retryables: [], - // retryablesClassic: [], - // deposits: [], - // childChainId: null - // }, - // l2ToL1MessagesToShow: [], - // parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, - // parentChainId: parentTxReceiptAndChainId?.parentChainId - // } - - // if (typeof getParentTxReceiptResult === 'undefined') { - // const res = await getChildToParentMessages(txHash) - // const { childTxStatus, childToParentMessages } = res - - // // TODO: handle terminal states - // if (childToParentMessages.length > 0) { - // return { - // ...defaultReturn, - // parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, - // parentChainId: parentTxReceiptAndChainId?.parentChainId, - // txHashState: ReceiptState.MESSAGES_FOUND, - // l2ToL1MessagesToShow: childToParentMessages - // } - // } - // if (childTxStatus === ChildTxStatus.SUCCESS) { - // return { - // ...defaultReturn, - // parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, - // parentChainId: parentTxReceiptAndChainId?.parentChainId, - // txHashState: ReceiptState.NO_L2_L1_MESSAGES - // } - // } - // if (childTxStatus === ChildTxStatus.FAILURE) { - // return { - // ...defaultReturn, - // parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, - // parentChainId: parentTxReceiptAndChainId?.parentChainId, - // txHashState: ReceiptState.L2_FAILED - // } - // } - - // return { - // ...defaultReturn, - // parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, - // parentChainId: parentTxReceiptAndChainId?.parentChainId, - // txHashState: ReceiptState.NOT_FOUND - // } - // } - - // const { parentTxReceipt: _parentTxReceipt, parentChainId } = - // getParentTxReceiptResult - // if ( - // _parentTxReceipt?.status === 0 || - // typeof _parentTxReceipt === 'undefined' - // ) { - // return { - // ...defaultReturn, - // parentTxReceipt: _parentTxReceipt, - // txHashState: ReceiptState.L1_FAILED - // } - // } - // console.log('getParentTxReceiptResult? ', getParentTxReceiptResult) - // console.log('_parentTxReceipt? ', _parentTxReceipt) - - // const allMessages = await getParentToChildMessagesAndDepositMessages( - // _parentTxReceipt, - // parentChainId - // ) - // console.log('allMessages? ', allMessages) - // const l1ToL2Messages = allMessages.retryables - // const l1ToL2MessagesClassic = allMessages.retryablesClassic - // const depositMessages = allMessages.deposits - // if ( - // l1ToL2Messages.length === 0 && - // l1ToL2MessagesClassic.length === 0 && - // depositMessages.length === 0 - // ) { - // return { - // ...defaultReturn, - // parentTxReceipt: _parentTxReceipt, - // parentChainId, - // txHashState: ReceiptState.NO_L1_L2_MESSAGES - // } - // } - - // return { - // ...defaultReturn, - // allMessages, - // parentTxReceipt: _parentTxReceipt, - // parentChainId, - // txHashState: ReceiptState.MESSAGES_FOUND - // } } diff --git a/packages/arb-token-bridge-ui/src/util/deposits/fetchEthDepositsFromEventLogs.ts b/packages/arb-token-bridge-ui/src/util/deposits/fetchEthDepositsFromEventLogs.ts new file mode 100644 index 0000000000..f767a7ea54 --- /dev/null +++ b/packages/arb-token-bridge-ui/src/util/deposits/fetchEthDepositsFromEventLogs.ts @@ -0,0 +1,121 @@ +import { BlockTag, Provider } from '@ethersproject/providers' + +export async function fetchEthDepositsFromEventLogs({ + sender, + fromBlock, + toBlock, + l1Provider +}: { + sender?: string + fromBlock: BlockTag + toBlock: BlockTag + l1Provider: Provider +}) { + if (typeof sender === 'undefined') { + return Promise.resolve([]) + } + + // funds received by this address + + const getParentTxReceiptResult = await getParentTxReceipt(txHash) + const parentTxReceiptAndChainId = getParentTxReceiptResult + const defaultReturn: { + allMessages: ParentToChildMessagesAndDepositMessages + l2ToL1MessagesToShow: ChildToParentMessageData[] + parentTxReceipt: ParentTransactionReceipt | undefined + parentChainId: number | undefined + } = { + allMessages: { + retryables: [], + retryablesClassic: [], + deposits: [], + childChainId: null + }, + l2ToL1MessagesToShow: [], + parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, + parentChainId: parentTxReceiptAndChainId?.parentChainId + } + + if (typeof getParentTxReceiptResult === 'undefined') { + const res = await getChildToParentMessages(txHash) + const { childTxStatus, childToParentMessages } = res + + // TODO: handle terminal states + if (childToParentMessages.length > 0) { + return { + ...defaultReturn, + parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, + parentChainId: parentTxReceiptAndChainId?.parentChainId, + txHashState: ReceiptState.MESSAGES_FOUND, + l2ToL1MessagesToShow: childToParentMessages + } + } + if (childTxStatus === ChildTxStatus.SUCCESS) { + return { + ...defaultReturn, + parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, + parentChainId: parentTxReceiptAndChainId?.parentChainId, + txHashState: ReceiptState.NO_L2_L1_MESSAGES + } + } + if (childTxStatus === ChildTxStatus.FAILURE) { + return { + ...defaultReturn, + parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, + parentChainId: parentTxReceiptAndChainId?.parentChainId, + txHashState: ReceiptState.L2_FAILED + } + } + + return { + ...defaultReturn, + parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, + parentChainId: parentTxReceiptAndChainId?.parentChainId, + txHashState: ReceiptState.NOT_FOUND + } + } + + const { parentTxReceipt: _parentTxReceipt, parentChainId } = + getParentTxReceiptResult + if ( + _parentTxReceipt?.status === 0 || + typeof _parentTxReceipt === 'undefined' + ) { + return { + ...defaultReturn, + parentTxReceipt: _parentTxReceipt, + txHashState: ReceiptState.L1_FAILED + } + } + console.log('getParentTxReceiptResult? ', getParentTxReceiptResult) + console.log('_parentTxReceipt? ', _parentTxReceipt) + + const allMessages = await getParentToChildMessagesAndDepositMessages( + _parentTxReceipt, + parentChainId + ) + console.log('allMessages? ', allMessages) + const l1ToL2Messages = allMessages.retryables + const l1ToL2MessagesClassic = allMessages.retryablesClassic + const depositMessages = allMessages.deposits + if ( + l1ToL2Messages.length === 0 && + l1ToL2MessagesClassic.length === 0 && + depositMessages.length === 0 + ) { + return { + ...defaultReturn, + parentTxReceipt: _parentTxReceipt, + parentChainId, + txHashState: ReceiptState.NO_L1_L2_MESSAGES + } + } + + return { + ...defaultReturn, + allMessages, + parentTxReceipt: _parentTxReceipt, + parentChainId, + txHashState: ReceiptState.MESSAGES_FOUND + } +} From 5239c68d3ecfca3db7a53e15302e3d983d1c8ae1 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Wed, 18 Dec 2024 18:46:20 +0000 Subject: [PATCH 43/49] wip --- .../getParentToChildEventsByTxHash.ts | 146 +++++++++--------- ...ParentToChildMessagesAndDepositMessages.ts | 4 +- .../deposits/fetchEthDepositsFromEventLogs.ts | 39 +---- 3 files changed, 75 insertions(+), 114 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts index 36335f9ecf..b69b94f7dc 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts @@ -1,7 +1,30 @@ import { BigNumber } from 'ethers' import { BlockTag } from '@ethersproject/providers' +import { EthDepositMessage, getArbitrumNetworks } from '@arbitrum/sdk' import { getParentTxReceipt } from '../helpers' +import { getProviderForChainId } from '@/token-bridge-sdk/utils' +import { normalizeTimestamp } from '../../../state/app/utils' + +export type FetchDepositTxFromEventLogResult = { + receiver: string + sender: string + timestamp: string + transactionHash: string + type: 'EthDeposit' | 'TokenDeposit' + isClassic: boolean + id: string + ethValue: string + tokenAmount?: string + blockCreatedAt: string + l1Token?: { + symbol: string + decimals: number + id: string + name: string + registeredAtBlock: string + } +} /** * Get event logs for ParentToChild transactions. @@ -19,97 +42,66 @@ import { getParentTxReceipt } from '../helpers' * @returns Any classic and nitro events that match the provided filters. */ export async function getParentToChildEventsByTxHash( - childChainId: number, + parentChainId: number, filter: { fromBlock: BlockTag; toBlock: BlockTag }, position?: BigNumber, destination?: string, txHash?: string, indexInBatch?: BigNumber -): Promise<(ParentToChildTransactionEvent & { transactionHash: string })[]> { +): Promise { if (!txHash) { return [] } - const getParentTxReceiptResult = await getParentTxReceipt( - txHash, - childChainId - ) + const parentTxReceipt = await getParentTxReceipt(txHash, parentChainId) - const childChain = await getArbitrumNetwork(childProvider) - const childNitroGenesisBlock = getNitroGenesisBlock(childChain) - - const inClassicRange = (blockTag: BlockTag, nitroGenBlock: number) => { - if (typeof blockTag === 'string') { - // taking classic of "earliest", "latest", "earliest" and the nitro gen block - // yields 0, nitro gen, nitro gen since the classic range is always between 0 and nitro gen - - switch (blockTag) { - case 'earliest': - return 0 - case 'latest': - return nitroGenBlock - case 'pending': - return nitroGenBlock - default: - throw new Error(`Unrecognised block tag. ${blockTag}`) - } - } - return Math.min(blockTag, nitroGenBlock) + const parentProvider = getProviderForChainId(parentChainId) + + if (!parentTxReceipt) { + return [] } - const inNitroRange = (blockTag: BlockTag, nitroGenBlock: number) => { - // taking nitro range of "earliest", "latest", "earliest" and the nitro gen block - // yields nitro gen, latest, pending since the nitro range is always between nitro gen and latest/pending - - if (typeof blockTag === 'string') { - switch (blockTag) { - case 'earliest': - return nitroGenBlock - case 'latest': - return 'latest' - case 'pending': - return 'pending' - default: - throw new Error(`Unrecognised block tag. ${blockTag}`) - } - } + // to test for child chain, filter chains that has this parent chain id as parent + // and then loop through it............. + const childChains = getArbitrumNetworks() + .filter(childChain => childChain.parentChainId === parentChainId) + .map(network => network.chainId) - return Math.max(blockTag, nitroGenBlock) - } + parentTxReceipt. - // only fetch nitro events after the genesis block - const classicFilter = { - fromBlock: inClassicRange(filter.fromBlock, childNitroGenesisBlock), - toBlock: inClassicRange(filter.toBlock, childNitroGenesisBlock) - } - const logQueries = [] - if (classicFilter.fromBlock !== classicFilter.toBlock) { - logQueries.push( - classic.ChildToParentMessageClassic.getChildToParentEvents( - childProvider, - classicFilter, - position, - destination, - hash, - indexInBatch - ) - ) - } + const parentToChildMessages = isClassic + ? await parentTxReceipt.getParentToChildMessagesClassic(childProvider) + : await parentTxReceipt.getParentToChildMessages(childProvider) - const nitroFilter = { - fromBlock: inNitroRange(filter.fromBlock, childNitroGenesisBlock), - toBlock: inNitroRange(filter.toBlock, childNitroGenesisBlock) - } - if (nitroFilter.fromBlock !== nitroFilter.toBlock) { - logQueries.push( - nitro.ChildToParentMessageNitro.getChildToParentEvents( - childProvider, - nitroFilter, - position, - destination, - hash - ) - ) + const parentToChildMessageReader = parentToChildMessages[0] + + if (!parentToChildMessageReader) { + return [] } - return (await Promise.all(logQueries)).flat(1) + const ethDeposits = await parentTxReceipt.getEthDeposits(childProvider) + + const timestamp = normalizeTimestamp( + (await parentProvider.getBlock(parentTxReceipt.blockNumber)).timestamp + ) + + const ethTransactions: FetchDepositTxFromEventLogResult[] = ethDeposits.map( + async depositMessage => { + const childProvider = getProviderForChainId(depositMessage.childChainId) + + const isClassic = await parentTxReceipt.isClassic(childProvider) + + return { + receiver: depositMessage.to, + sender: depositMessage.from, + timestamp, + transactionHash: depositMessage. + type: 'EthDeposit', + isClassic, + ethValue: depositMessage.value.toString(), + blockCreatedAt: parentTxReceipt.blockNumber.toString() + } + } + ) + + return transaction } diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages.ts index 6ceb9c1bc4..671f967562 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages.ts @@ -15,7 +15,9 @@ export const getParentToChildMessagesAndDepositMessages = async ( parentChainId: number ): Promise => { try { - const childNetworks = getArbitrumNetworks().map(network => network.chainId) + const childNetworks = getArbitrumNetworks() + .filter(childChain => childChain.parentChainId === parentChainId) + .map(network => network.chainId) const messagesPromises = childNetworks.map(async childChainId => { // TODO: error handle const childNetwork = await getArbitrumNetwork(childChainId) diff --git a/packages/arb-token-bridge-ui/src/util/deposits/fetchEthDepositsFromEventLogs.ts b/packages/arb-token-bridge-ui/src/util/deposits/fetchEthDepositsFromEventLogs.ts index f767a7ea54..c73dbd4d33 100644 --- a/packages/arb-token-bridge-ui/src/util/deposits/fetchEthDepositsFromEventLogs.ts +++ b/packages/arb-token-bridge-ui/src/util/deposits/fetchEthDepositsFromEventLogs.ts @@ -1,4 +1,6 @@ import { BlockTag, Provider } from '@ethersproject/providers' +import { getParentToChildMessagesAndDepositMessages } from '../../components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages' +import { ReceiptState } from '../../components/TransactionHistory/TransactionHistoryTxHashSearch/helpers' export async function fetchEthDepositsFromEventLogs({ sender, @@ -37,42 +39,7 @@ export async function fetchEthDepositsFromEventLogs({ } if (typeof getParentTxReceiptResult === 'undefined') { - const res = await getChildToParentMessages(txHash) - const { childTxStatus, childToParentMessages } = res - - // TODO: handle terminal states - if (childToParentMessages.length > 0) { - return { - ...defaultReturn, - parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, - parentChainId: parentTxReceiptAndChainId?.parentChainId, - txHashState: ReceiptState.MESSAGES_FOUND, - l2ToL1MessagesToShow: childToParentMessages - } - } - if (childTxStatus === ChildTxStatus.SUCCESS) { - return { - ...defaultReturn, - parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, - parentChainId: parentTxReceiptAndChainId?.parentChainId, - txHashState: ReceiptState.NO_L2_L1_MESSAGES - } - } - if (childTxStatus === ChildTxStatus.FAILURE) { - return { - ...defaultReturn, - parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, - parentChainId: parentTxReceiptAndChainId?.parentChainId, - txHashState: ReceiptState.L2_FAILED - } - } - - return { - ...defaultReturn, - parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, - parentChainId: parentTxReceiptAndChainId?.parentChainId, - txHashState: ReceiptState.NOT_FOUND - } + return undefined } const { parentTxReceipt: _parentTxReceipt, parentChainId } = From 1b06afffeb216fced7946e156031a8553e1add38 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Thu, 19 Dec 2024 19:43:51 +0000 Subject: [PATCH 44/49] add more code --- .../getParentToChildEventsByTxHash.ts | 87 +++++++++++++------ 1 file changed, 59 insertions(+), 28 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts index b69b94f7dc..d8a9bdb74e 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts @@ -5,6 +5,7 @@ import { EthDepositMessage, getArbitrumNetworks } from '@arbitrum/sdk' import { getParentTxReceipt } from '../helpers' import { getProviderForChainId } from '@/token-bridge-sdk/utils' import { normalizeTimestamp } from '../../../state/app/utils' +import { ParentToChildMessagesAndDepositMessages } from './helpers' export type FetchDepositTxFromEventLogResult = { receiver: string @@ -60,48 +61,78 @@ export async function getParentToChildEventsByTxHash( return [] } - // to test for child chain, filter chains that has this parent chain id as parent - // and then loop through it............. - const childChains = getArbitrumNetworks() - .filter(childChain => childChain.parentChainId === parentChainId) - .map(network => network.chainId) + const childChainMessages = getArbitrumNetworks() + .filter(childChain => childChain.parentChainId === parentChainId) + .map(async childChain => { + const childChainId = childChain.chainId + const childProvider = getProviderForChainId(childChainId) - parentTxReceipt. + // Check if any parentToChild msg is sent to the inbox of this child chain + const logFromChildChainInbox = parentTxReceipt.logs.filter( + log => + log.address.toLowerCase() === childChain.ethBridge.inbox.toLowerCase() + ) - const parentToChildMessages = isClassic - ? await parentTxReceipt.getParentToChildMessagesClassic(childProvider) - : await parentTxReceipt.getParentToChildMessages(childProvider) + if (logFromChildChainInbox.length === 0) { + return + } - const parentToChildMessageReader = parentToChildMessages[0] + const isClassic = await parentTxReceipt.isClassic(childProvider) - if (!parentToChildMessageReader) { - return [] - } + const parentToChildMessages = isClassic + ? await parentTxReceipt.getParentToChildMessagesClassic(childProvider) + : await parentTxReceipt.getParentToChildMessages(childProvider) - const ethDeposits = await parentTxReceipt.getEthDeposits(childProvider) + const ethDeposits = await parentTxReceipt.getEthDeposits(childProvider) - const timestamp = normalizeTimestamp( - (await parentProvider.getBlock(parentTxReceipt.blockNumber)).timestamp - ) + const parentToChildMessageReader = parentToChildMessages[0] - const ethTransactions: FetchDepositTxFromEventLogResult[] = ethDeposits.map( - async depositMessage => { - const childProvider = getProviderForChainId(depositMessage.childChainId) + return { + ethDeposits + } + }) - const isClassic = await parentTxReceipt.isClassic(childProvider) + const messages = await Promise.all(childChainMessages) + const allMessages = messages.reduce( + (acc, value) => { + if (!value) { + return acc + } + return { + // retryables: acc.retryables.concat(value.allL1ToL2Messages), + // retryablesClassic: acc.retryablesClassic.concat( + // value.allL1ToL2MessagesClassic + // ), + ethDeposits: acc.deposits.concat(value.ethDeposits) + } + }, + { + retryables: [], + retryablesClassic: [], + deposits: [] + } as ParentToChildMessagesAndDepositMessages + ) + + const ethTransactions: FetchDepositTxFromEventLogResult[] = + allMessages?.ethDeposits.map(async depositMessage => { + const timestamp = normalizeTimestamp( + (await parentProvider.getBlock(parentTxReceipt.blockNumber)).timestamp + ) return { receiver: depositMessage.to, sender: depositMessage.from, - timestamp, - transactionHash: depositMessage. + timestamp: timestamp.toString(), + transactionHash: depositMessage.childTxHash, type: 'EthDeposit', - isClassic, + isClassic: false, + id: depositMessage.childTxHash, ethValue: depositMessage.value.toString(), blockCreatedAt: parentTxReceipt.blockNumber.toString() - } - } - ) + } as FetchDepositTxFromEventLogResult + }) - return transaction + return { + ethTransactions + } } From 52f7bedfcc148aac6bdcaf79e8113ce6827a2d48 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Fri, 20 Dec 2024 18:07:23 +0000 Subject: [PATCH 45/49] update fn wip --- .../TransactionHistoryTxHashSearchResult.tsx | 2 +- .../getParentToChildEventsByTxHash.ts | 394 ++++++++++++++---- .../TransactionHistoryTxHashSearch/helpers.ts | 21 +- .../deposits/fetchDepositTxFromSubgraph.ts | 7 +- .../src/util/deposits/fetchDeposits.ts | 13 + .../deposits/fetchEthDepositsFromEventLogs.ts | 88 ---- 6 files changed, 345 insertions(+), 180 deletions(-) delete mode 100644 packages/arb-token-bridge-ui/src/util/deposits/fetchEthDepositsFromEventLogs.ts diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx index 1294efd683..057e7f6d5e 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx @@ -76,7 +76,7 @@ export function TransactionHistoryTxHashSearchResult() { } updateTx() - }, [tx]) + }, [tx, txFromHash]) if (!transactions) { return null diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts index d8a9bdb74e..bb63da632f 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts @@ -1,21 +1,41 @@ -import { BigNumber } from 'ethers' -import { BlockTag } from '@ethersproject/providers' -import { EthDepositMessage, getArbitrumNetworks } from '@arbitrum/sdk' +import { BigNumber, providers, utils } from 'ethers' +import { BlockTag, Provider } from '@ethersproject/providers' +import { + DepositInitiatedEvent, + L1ERC20Gateway +} from '@arbitrum/sdk/dist/lib/abi/L1ERC20Gateway' +import { L1ERC20Gateway__factory } from '@arbitrum/sdk/dist/lib/abi/factories/L1ERC20Gateway__factory' +import { ERC20__factory } from '@arbitrum/sdk/dist/lib/abi/factories/ERC20__factory' +import { + ArbitrumNetwork, + ChildTransactionReceipt, + EventFetcher, + getArbitrumNetwork, + getArbitrumNetworks, + ParentTransactionReceipt +} from '@arbitrum/sdk' import { getParentTxReceipt } from '../helpers' -import { getProviderForChainId } from '@/token-bridge-sdk/utils' +import { + getChainIdFromProvider, + getProviderForChainId +} from '@/token-bridge-sdk/utils' import { normalizeTimestamp } from '../../../state/app/utils' import { ParentToChildMessagesAndDepositMessages } from './helpers' +import { Transaction } from '../../../types/Transactions' +import { updateAdditionalDepositData } from '../../../util/deposits/helpers' +import { AssetType } from '../../../hooks/arbTokenBridge.types' +import { fetchNativeCurrency } from '../../../hooks/useNativeCurrency' export type FetchDepositTxFromEventLogResult = { receiver: string sender: string - timestamp: string + timestampCreated: string transactionHash: string type: 'EthDeposit' | 'TokenDeposit' isClassic: boolean id: string - ethValue: string + ethValue?: string tokenAmount?: string blockCreatedAt: string l1Token?: { @@ -27,6 +47,95 @@ export type FetchDepositTxFromEventLogResult = { } } +const getDepositInitiatedLogs = async ({ + fromBlock, + toBlock, + parentChainProvider, + childChain +}: { + fromBlock: number + toBlock: number + parentChainProvider: Provider + childChain: ArbitrumNetwork +}) => { + const [ + depositsInitiatedLogsL1Erc20Gateway, + depositsInitiatedLogsL1CustomGateway, + depositsInitiatedLogsL1WethGateway + ] = await Promise.all( + [ + childChain.tokenBridge!.parentErc20Gateway, + childChain.tokenBridge!.parentCustomGateway, + childChain.tokenBridge!.parentWethGateway + ].map(gatewayAddress => { + return getDepositInitiatedEventData( + gatewayAddress, + { fromBlock, toBlock }, + parentChainProvider + ) + }) + ) + + return [ + ...(depositsInitiatedLogsL1Erc20Gateway || []), + ...(depositsInitiatedLogsL1CustomGateway || []), + ...(depositsInitiatedLogsL1WethGateway || []) + ] +} + +const getDepositInitiatedEventData = async ( + parentChainGatewayAddress: string, + filter: { + fromBlock: providers.BlockTag + toBlock: providers.BlockTag + }, + parentChainProvider: providers.Provider +) => { + const eventFetcher = new EventFetcher(parentChainProvider) + const logs = await eventFetcher.getEvents< + L1ERC20Gateway, + DepositInitiatedEvent + >(L1ERC20Gateway__factory, (g: any) => g.filters.DepositInitiated(), { + ...filter, + address: parentChainGatewayAddress + }) + + return logs +} + +async function getChildChainMessages( + childChain: ArbitrumNetwork, + parentTxReceipt: ParentTransactionReceipt +) { + const childChainId = childChain.chainId + const childProvider = getProviderForChainId(childChainId) + + // Check if any parentToChild msg is sent to the inbox of this child chain + const logFromChildChainInbox = parentTxReceipt.logs.filter( + log => + log.address.toLowerCase() === childChain.ethBridge.inbox.toLowerCase() + ) + + if (logFromChildChainInbox.length === 0) { + return + } + + const parentToChildMessagesClassic = + await parentTxReceipt.getParentToChildMessagesClassic(childProvider) + + const parentToChildMessages = await parentTxReceipt.getParentToChildMessages( + childProvider + ) + + const ethDeposits = await parentTxReceipt.getEthDeposits(childProvider) + + return { + tokenDepositRetryables: parentToChildMessages, + tokenDepositRetryablesClassic: parentToChildMessagesClassic, + ethDeposits + } +} + /** * Get event logs for ParentToChild transactions. * Only support by tx hash for now. @@ -42,97 +151,242 @@ export type FetchDepositTxFromEventLogResult = { * @param indexInBatch The index in the batch, only valid for pre-nitro events. This parameter is ignored post-nitro * @returns Any classic and nitro events that match the provided filters. */ -export async function getParentToChildEventsByTxHash( - parentChainId: number, - filter: { fromBlock: BlockTag; toBlock: BlockTag }, - position?: BigNumber, - destination?: string, - txHash?: string, +export async function getParentToChildEventsByTxHash({ + parentChainId, + filter, + position, + destination, + txHash, + indexInBatch +}: { + parentChainId: number + filter?: { fromBlock: BlockTag; toBlock: BlockTag } + position?: BigNumber + destination?: string + txHash?: string indexInBatch?: BigNumber -): Promise { +}): Promise<{ + ethDeposits: Transaction[] + tokenDepositRetryables: Transaction[] +}> { if (!txHash) { - return [] + return { + ethDeposits: [], + tokenDepositRetryables: [] + } } const parentTxReceipt = await getParentTxReceipt(txHash, parentChainId) const parentProvider = getProviderForChainId(parentChainId) if (!parentTxReceipt) { - return [] + return { + ethDeposits: [], + tokenDepositRetryables: [] + } } const childChainMessages = getArbitrumNetworks() .filter(childChain => childChain.parentChainId === parentChainId) - .map(async childChain => { - const childChainId = childChain.chainId - const childProvider = getProviderForChainId(childChainId) - - // Check if any parentToChild msg is sent to the inbox of this child chain - const logFromChildChainInbox = parentTxReceipt.logs.filter( - log => - log.address.toLowerCase() === childChain.ethBridge.inbox.toLowerCase() - ) + .map(childChain => getChildChainMessages(childChain, parentTxReceipt)) - if (logFromChildChainInbox.length === 0) { - return - } + const messages = await Promise.all(childChainMessages) - const isClassic = await parentTxReceipt.isClassic(childProvider) + if (typeof messages === 'undefined') { + return { ethDeposits: [], tokenDepositRetryables: [] } + } - const parentToChildMessages = isClassic - ? await parentTxReceipt.getParentToChildMessagesClassic(childProvider) - : await parentTxReceipt.getParentToChildMessages(childProvider) + const allMessages: ParentToChildMessagesAndDepositMessages = messages + .filter(message => typeof message !== 'undefined') + .reduce( + (acc, value) => { + if (typeof acc === 'undefined') { + return { + tokenDepositRetryables: [], + tokenDepositRetryablesClassic: [], + ethDeposits: [] + } + } + if (typeof value === 'undefined') { + return acc + } + return { + tokenDepositRetryables: acc.tokenDepositRetryables.concat( + value.tokenDepositRetryables + ), + tokenDepositRetryablesClassic: + acc.tokenDepositRetryablesClassic.concat( + value.tokenDepositRetryablesClassic + ), + ethDeposits: acc.ethDeposits.concat(value.ethDeposits) + } + }, + { + tokenDepositRetryables: [], + tokenDepositRetryablesClassic: [], + ethDeposits: [] + } as ParentToChildMessagesAndDepositMessages + ) - const ethDeposits = await parentTxReceipt.getEthDeposits(childProvider) + const tokenDepositRetryables: Transaction[] = await Promise.all( + allMessages.tokenDepositRetryables + .map(async tokenDepositMessage => { + const retryableReceipt = + await tokenDepositMessage.getRetryableCreationReceipt() + if (!retryableReceipt) { + return Promise.resolve([]) + } + const childChainId = await getChainIdFromProvider( + tokenDepositMessage.childProvider + ) + const childChain = getArbitrumNetwork(childChainId) - const parentToChildMessageReader = parentToChildMessages[0] + const childChainTxReceipt = new ChildTransactionReceipt( + retryableReceipt + ) + const [event] = childChainTxReceipt.getChildToParentEvents() - return { - ethDeposits - } - }) + const receiver = await tokenDepositMessage.getBeneficiary() - const messages = await Promise.all(childChainMessages) + let parentChainErc20Address: string | undefined, + tokenAmount: string | undefined, + tokenDepositData: + | { + tokenAmount: string | undefined + l1Token: { + symbol: string + decimals: number + id: string + name: string + } + } + | undefined - const allMessages = messages.reduce( - (acc, value) => { - if (!value) { - return acc - } - return { - // retryables: acc.retryables.concat(value.allL1ToL2Messages), - // retryablesClassic: acc.retryablesClassic.concat( - // value.allL1ToL2MessagesClassic - // ), - ethDeposits: acc.deposits.concat(value.ethDeposits) - } - }, - { - retryables: [], - retryablesClassic: [], - deposits: [] - } as ParentToChildMessagesAndDepositMessages + try { + if (!event) { + return Promise.resolve([]) + } + const retryableMessageData = event.data + const retryableBody = retryableMessageData.split('0xc9f95d32')[1] + const requestId = '0x' + retryableBody?.slice(0, 64) + const depositsInitiatedLogs = parentTxReceipt.logs.filter( + log => + log.address.toLowerCase() === + childChain.ethBridge.inbox.toLowerCase() + ) + const depositsInitiatedEvent = depositsInitiatedLogs.find( + log => log.topics[3] === requestId + ) + // @ts-ignore + parentChainErc20Address = depositsInitiatedEvent?.event[0] + // @ts-ignore + tokenAmount = depositsInitiatedEvent?.event[4]?.toString() + } catch (e) { + console.log(e) + } + + const parentChainProvider = getProviderForChainId(parentChainId) + + if (parentChainErc20Address) { + try { + const erc20 = ERC20__factory.connect( + parentChainErc20Address, + parentChainProvider + ) + const [name, symbol, decimals] = await Promise.all([ + erc20.name(), + erc20.symbol(), + erc20.decimals() + ]) + tokenDepositData = { + tokenAmount, + l1Token: { + symbol, + decimals, + id: parentChainErc20Address, + name + } + } + } catch (e) { + console.log('failed to fetch token data', e) + } + } + + const depositTx: Transaction = { + destination: receiver, + sender: tokenDepositMessage.sender, + timestampCreated: event?.timestamp.toString(), + type: 'deposit', + isClassic: false, + txID: retryableReceipt.transactionHash, + value: + typeof tokenDepositData !== 'undefined' && + typeof tokenDepositData.tokenAmount !== 'undefined' + ? utils.formatUnits( + tokenDepositData.tokenAmount, + tokenDepositData.l1Token.decimals + ) + : null, + blockNumber: parentTxReceipt.blockNumber, + direction: 'deposit', + source: 'event_logs', + assetType: AssetType.ERC20, + assetName: tokenDepositData?.l1Token.name ?? '', + parentChainId, + l1NetworkID: parentChainId.toString(), + childChainId + } + + const childProvider = getProviderForChainId(childChainId) + + return await updateAdditionalDepositData({ + depositTx, + parentProvider, + childProvider + }) + }) + .filter( + (eventLogResult): eventLogResult is Promise => + typeof eventLogResult !== 'undefined' + ) ) - const ethTransactions: FetchDepositTxFromEventLogResult[] = - allMessages?.ethDeposits.map(async depositMessage => { + const ethDeposits: Transaction[] = await Promise.all( + allMessages.ethDeposits.map(async depositMessage => { const timestamp = normalizeTimestamp( (await parentProvider.getBlock(parentTxReceipt.blockNumber)).timestamp ) - return { - receiver: depositMessage.to, + const childProvider = getProviderForChainId(depositMessage.childChainId) + const nativeCurrency = await fetchNativeCurrency({ + provider: childProvider + }) + const depositTx: Transaction = { + destination: depositMessage.to, sender: depositMessage.from, - timestamp: timestamp.toString(), - transactionHash: depositMessage.childTxHash, - type: 'EthDeposit', + timestampCreated: timestamp.toString(), + txID: depositMessage.childTxHash, + type: 'deposit', isClassic: false, - id: depositMessage.childTxHash, - ethValue: depositMessage.value.toString(), - blockCreatedAt: parentTxReceipt.blockNumber.toString() - } as FetchDepositTxFromEventLogResult + value: utils.formatUnits( + depositMessage.value.toString(), + nativeCurrency.decimals + ), + blockNumber: parentTxReceipt.blockNumber, + direction: 'deposit', + source: 'event_logs', + parentChainId, + l1NetworkID: parentChainId.toString(), + childChainId: depositMessage.childChainId, + assetType: AssetType.ETH, + assetName: nativeCurrency.name + } + return await updateAdditionalDepositData({ + depositTx, + parentProvider, + childProvider + }) }) + ) - return { - ethTransactions - } + return { ethDeposits, tokenDepositRetryables } } diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts index a7fe0bdc87..b44182dbdd 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts @@ -34,25 +34,10 @@ export enum ReceiptState { NO_L2_L1_MESSAGES } -export interface ParentToChildMessageReaderWithNetwork - extends ParentToChildMessageReader { - childNetwork: ArbitrumNetwork -} - -export interface ParentToChildMessageReaderClassicWithNetwork - extends ParentToChildMessageReaderClassic { - childNetwork: ArbitrumNetwork -} - -export interface EthDepositMessageWithNetwork extends EthDepositMessage { - childNetwork: ArbitrumNetwork -} - export interface ParentToChildMessagesAndDepositMessages { - retryables: ParentToChildMessageReaderWithNetwork[] - retryablesClassic: ParentToChildMessageReaderClassicWithNetwork[] - deposits: EthDepositMessageWithNetwork[] - childChainId: number | null + tokenDepositRetryables: ParentToChildMessageReader[] + tokenDepositRetryablesClassic: ParentToChildMessageReaderClassic[] + ethDeposits: EthDepositMessage[] } export interface ChildToParentMessageData { diff --git a/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts b/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts index bd38a37537..fc7fc23e03 100644 --- a/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts +++ b/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts @@ -1,10 +1,10 @@ -import { getArbitrumNetworks } from '@arbitrum/sdk' +import { getArbitrumNetwork, getArbitrumNetworks } from '@arbitrum/sdk' import { Address } from 'wagmi' import { FetchDepositsFromSubgraphResult } from './fetchDepositsFromSubgraph' import { getAPIBaseUrl, sanitizeQueryParams } from '..' import { hasL1Subgraph } from '../SubgraphUtils' -import { getNetworkName } from '../networks' +import { ChainId, getNetworkName } from '../networks' import { fetchNativeCurrency } from '../../hooks/useNativeCurrency' import { mapDepositsFromSubgraph } from './mapDepositsFromSubgraph' import { Transaction } from '../../types/Transactions' @@ -20,7 +20,8 @@ export async function fetchDepositTxFromSubgraph( txHash: string, connectedAddress: Address ): Promise { - const supportedChildChains = getArbitrumNetworks() + // TODO: test with Arbitrum Sepolia + const supportedChildChains = [getArbitrumNetwork(ChainId.ArbitrumSepolia)] const fetcherList = supportedChildChains.map(childChain => { const childChainId = childChain.chainId diff --git a/packages/arb-token-bridge-ui/src/util/deposits/fetchDeposits.ts b/packages/arb-token-bridge-ui/src/util/deposits/fetchDeposits.ts index 2d23129cfd..8953e44ae8 100644 --- a/packages/arb-token-bridge-ui/src/util/deposits/fetchDeposits.ts +++ b/packages/arb-token-bridge-ui/src/util/deposits/fetchDeposits.ts @@ -13,6 +13,8 @@ import { FetchEthDepositsToCustomDestinationFromSubgraphResult } from './fetchEthDepositsToCustomDestinationFromSubgraph' import { mapDepositsFromSubgraph } from './mapDepositsFromSubgraph' +import { getParentToChildEventsByTxHash } from '../../components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash' +import { DepositInitiatedEvent } from '@arbitrum/sdk/dist/lib/abi/L1ERC20Gateway' export type FetchDepositParams = { sender?: string @@ -119,7 +121,18 @@ export const fetchDeposits = async ({ } ) + const { + ethDeposits: ethDepositsFromEventLogs, + tokenDepositRetryables: tokenDepositsFromEventLogs + } = await getParentToChildEventsByTxHash({ + parentChainId: l1ChainId, + txHash: searchString, + filter: { fromBlock, toBlock: toBlock ?? 'latest' } + }) + return [ + ...ethDepositsFromEventLogs, + ...tokenDepositsFromEventLogs, ...mappedDepositsFromSubgraph, ...mappedEthDepositsToCustomDestinationFromSubgraph ].sort((a, b) => Number(b.timestampCreated) - Number(a.timestampCreated)) diff --git a/packages/arb-token-bridge-ui/src/util/deposits/fetchEthDepositsFromEventLogs.ts b/packages/arb-token-bridge-ui/src/util/deposits/fetchEthDepositsFromEventLogs.ts deleted file mode 100644 index c73dbd4d33..0000000000 --- a/packages/arb-token-bridge-ui/src/util/deposits/fetchEthDepositsFromEventLogs.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { BlockTag, Provider } from '@ethersproject/providers' -import { getParentToChildMessagesAndDepositMessages } from '../../components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages' -import { ReceiptState } from '../../components/TransactionHistory/TransactionHistoryTxHashSearch/helpers' - -export async function fetchEthDepositsFromEventLogs({ - sender, - fromBlock, - toBlock, - l1Provider -}: { - sender?: string - fromBlock: BlockTag - toBlock: BlockTag - l1Provider: Provider -}) { - if (typeof sender === 'undefined') { - return Promise.resolve([]) - } - - // funds received by this address - - const getParentTxReceiptResult = await getParentTxReceipt(txHash) - const parentTxReceiptAndChainId = getParentTxReceiptResult - const defaultReturn: { - allMessages: ParentToChildMessagesAndDepositMessages - l2ToL1MessagesToShow: ChildToParentMessageData[] - parentTxReceipt: ParentTransactionReceipt | undefined - parentChainId: number | undefined - } = { - allMessages: { - retryables: [], - retryablesClassic: [], - deposits: [], - childChainId: null - }, - l2ToL1MessagesToShow: [], - parentTxReceipt: parentTxReceiptAndChainId?.parentTxReceipt, - parentChainId: parentTxReceiptAndChainId?.parentChainId - } - - if (typeof getParentTxReceiptResult === 'undefined') { - return undefined - } - - const { parentTxReceipt: _parentTxReceipt, parentChainId } = - getParentTxReceiptResult - if ( - _parentTxReceipt?.status === 0 || - typeof _parentTxReceipt === 'undefined' - ) { - return { - ...defaultReturn, - parentTxReceipt: _parentTxReceipt, - txHashState: ReceiptState.L1_FAILED - } - } - console.log('getParentTxReceiptResult? ', getParentTxReceiptResult) - console.log('_parentTxReceipt? ', _parentTxReceipt) - - const allMessages = await getParentToChildMessagesAndDepositMessages( - _parentTxReceipt, - parentChainId - ) - console.log('allMessages? ', allMessages) - const l1ToL2Messages = allMessages.retryables - const l1ToL2MessagesClassic = allMessages.retryablesClassic - const depositMessages = allMessages.deposits - if ( - l1ToL2Messages.length === 0 && - l1ToL2MessagesClassic.length === 0 && - depositMessages.length === 0 - ) { - return { - ...defaultReturn, - parentTxReceipt: _parentTxReceipt, - parentChainId, - txHashState: ReceiptState.NO_L1_L2_MESSAGES - } - } - - return { - ...defaultReturn, - allMessages, - parentTxReceipt: _parentTxReceipt, - parentChainId, - txHashState: ReceiptState.MESSAGES_FOUND - } -} From a5d7e212f60a70a9b5fe57cbd819e238bdd430ad Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Mon, 23 Dec 2024 18:56:03 +0000 Subject: [PATCH 46/49] update --- .../TransactionHistory/TransactionHistory.tsx | 3 + .../TransactionHistoryTxHashSearchResult.tsx | 49 +++---- .../getParentToChildEventsByTxHash.ts | 42 +++--- .../TransactionsTableRow.tsx | 5 +- .../src/hooks/useTransactionHistory.ts | 6 +- .../deposits/fetchDepositTxFromSubgraph.ts | 123 ++++-------------- .../src/util/deposits/fetchDeposits.ts | 26 ++-- 7 files changed, 94 insertions(+), 160 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 06f723ae31..5e55c6f28f 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistory.tsx @@ -4,6 +4,7 @@ import { MergedTransaction } from '../../state/app/state' import { TransactionHistorySearchBar } from './TransactionHistorySearchBar' import { TransactionHistoryTxHashSearchResult } from './TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult' import { TransactionHistoryAddressSearchResults } from './TransactionHistoryAddressSearchResults' +import { TransactionsTableDetails } from './TransactionsTableDetails' type TxDetailsStore = { tx: MergedTransaction | null @@ -39,6 +40,8 @@ export const TransactionHistory = () => { + + ) } diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx index 057e7f6d5e..c591887cb3 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/TransactionHistoryTxHashSearchResult.tsx @@ -1,22 +1,20 @@ import { Column, Table } from 'react-virtualized' -import { useEffect, useMemo, useRef, useState } from 'react' +import { useEffect, useMemo, useRef } from 'react' import { twMerge } from 'tailwind-merge' import useSWR from 'swr' import { ContentWrapper, TableHeader } from '../TransactionHistoryTable' import { useTransactionHistoryAddressStore } from '../TransactionHistorySearchBar' import { TransactionsTableRow } from '../TransactionsTableRow' -import { MergedTransaction } from '../../../state/app/state' -import { fetchDepositTxFromSubgraph } from '../../../util/deposits/fetchDepositTxFromSubgraph' +import { fetchDepositByTxHash } from '../../../util/deposits/fetchDepositTxFromSubgraph' import { useTransactionHistory } from '../../../hooks/useTransactionHistory' -import { getUpdatedEthDeposit, isTxPending } from '../helpers' export function TransactionHistoryTxHashSearchResult() { const contentWrapperRef = useRef(null) const tableRef = useRef
(null) const { sanitizedAddress, sanitizedTxHash } = useTransactionHistoryAddressStore() - const [tx, setTx] = useState() + const { updatePendingTransaction } = useTransactionHistory(sanitizedAddress) const queryKey = useMemo(() => { if (!sanitizedTxHash || !sanitizedAddress) { @@ -33,9 +31,19 @@ export function TransactionHistoryTxHashSearchResult() { isLoading, error } = useSWR(queryKey, ([txHash, connectedAddress]) => - fetchDepositTxFromSubgraph(txHash, connectedAddress) + fetchDepositByTxHash(txHash, connectedAddress) ) + useEffect(() => { + if (isLoading || !transactions || transactions.length === 0) { + console.log('still loading') + return + } + console.log('error? ', error) + console.log('transactions: ', transactions) + transactions.forEach(updatePendingTransaction) + }, [transactions, isLoading, error, updatePendingTransaction]) + const TABLE_HEADER_HEIGHT = 52 const TABLE_ROW_HEIGHT = 60 @@ -55,31 +63,8 @@ export function TransactionHistoryTxHashSearchResult() { ) }, [contentWrapperRef.current?.offsetTop]) - const txFromHash = - typeof transactions === 'undefined' - ? null - : (transactions[0] as unknown as MergedTransaction) - - console.log('txFromHash: ', txFromHash) - - useEffect(() => { - async function updateTx() { - if (!txFromHash) { - return - } - const updatedTx = await getUpdatedEthDeposit(txFromHash) - if (updatedTx) { - setTx(updatedTx) - - console.log('updatedTx: ', updatedTx) - } - } - - updateTx() - }, [tx, txFromHash]) - if (!transactions) { - return null + return <>Loading... } return ( @@ -102,12 +87,12 @@ export function TransactionHistoryTxHashSearchResult() { className="table-auto last:border-b-0" rowGetter={({ index }) => transactions[index]} rowRenderer={({ index, style }) => { + const tx = transactions[index] + if (!tx) { return null } - console.log('tx here!', tx) - const isLastRow = index + 1 === transactions.length const key = `${tx.parentChainId}-${tx.childChainId}-${tx.txId}` diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts index bb63da632f..99d7eac792 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts @@ -12,6 +12,8 @@ import { EventFetcher, getArbitrumNetwork, getArbitrumNetworks, + ParentToChildMessageReader, + ParentToChildMessageReaderClassic, ParentTransactionReceipt } from '@arbitrum/sdk' @@ -20,7 +22,6 @@ import { getChainIdFromProvider, getProviderForChainId } from '@/token-bridge-sdk/utils' -import { normalizeTimestamp } from '../../../state/app/utils' import { ParentToChildMessagesAndDepositMessages } from './helpers' import { Transaction } from '../../../types/Transactions' import { updateAdditionalDepositData } from '../../../util/deposits/helpers' @@ -120,12 +121,15 @@ async function getChildChainMessages( return } - const parentToChildMessagesClassic = - await parentTxReceipt.getParentToChildMessagesClassic(childProvider) + const isClassic = await parentTxReceipt.isClassic(childProvider) - const parentToChildMessages = await parentTxReceipt.getParentToChildMessages( - childProvider - ) + const parentToChildMessagesClassic = isClassic + ? await parentTxReceipt.getParentToChildMessagesClassic(childProvider) + : ([] as ParentToChildMessageReaderClassic[]) + + const parentToChildMessages = isClassic + ? ([] as ParentToChildMessageReader[]) + : await parentTxReceipt.getParentToChildMessages(childProvider) const ethDeposits = await parentTxReceipt.getEthDeposits(childProvider) @@ -314,11 +318,11 @@ export async function getParentToChildEventsByTxHash({ const depositTx: Transaction = { destination: receiver, - sender: tokenDepositMessage.sender, + sender: parentTxReceipt.from, timestampCreated: event?.timestamp.toString(), type: 'deposit', isClassic: false, - txID: retryableReceipt.transactionHash, + txID: parentTxReceipt.transactionHash, value: typeof tokenDepositData !== 'undefined' && typeof tokenDepositData.tokenAmount !== 'undefined' @@ -333,8 +337,9 @@ export async function getParentToChildEventsByTxHash({ assetType: AssetType.ERC20, assetName: tokenDepositData?.l1Token.name ?? '', parentChainId, + childChainId, l1NetworkID: parentChainId.toString(), - childChainId + l2NetworkID: childChainId.toString() } const childProvider = getProviderForChainId(childChainId) @@ -353,19 +358,19 @@ export async function getParentToChildEventsByTxHash({ const ethDeposits: Transaction[] = await Promise.all( allMessages.ethDeposits.map(async depositMessage => { - const timestamp = normalizeTimestamp( - (await parentProvider.getBlock(parentTxReceipt.blockNumber)).timestamp - ) + const timestamp = ( + await parentProvider.getBlock(parentTxReceipt.blockNumber) + ).timestamp const childProvider = getProviderForChainId(depositMessage.childChainId) const nativeCurrency = await fetchNativeCurrency({ provider: childProvider }) const depositTx: Transaction = { - destination: depositMessage.to, - sender: depositMessage.from, + destination: depositMessage.to.toLowerCase(), + sender: parentTxReceipt.from.toLowerCase(), timestampCreated: timestamp.toString(), - txID: depositMessage.childTxHash, - type: 'deposit', + txID: parentTxReceipt.transactionHash, + type: 'deposit-l1', isClassic: false, value: utils.formatUnits( depositMessage.value.toString(), @@ -375,10 +380,11 @@ export async function getParentToChildEventsByTxHash({ direction: 'deposit', source: 'event_logs', parentChainId, - l1NetworkID: parentChainId.toString(), childChainId: depositMessage.childChainId, + l1NetworkID: parentChainId.toString(), + l2NetworkID: depositMessage.childChainId.toString(), assetType: AssetType.ETH, - assetName: nativeCurrency.name + assetName: nativeCurrency.symbol } return await updateAdditionalDepositData({ depositTx, diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRow.tsx b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRow.tsx index 01f887a666..124e596bda 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRow.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionsTableRow.tsx @@ -264,7 +264,10 @@ export function TransactionsTableRow({ aria-label="Transaction details button" variant="primary" className="rounded border border-white p-2 text-xs text-white" - onClick={() => openTxDetails(tx)} + onClick={() => { + console.log('clicked See Details: ', tx) + openTxDetails(tx) + }} > See Details diff --git a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts index 285d867585..7a9933c67a 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts @@ -147,7 +147,9 @@ function isDeposit(tx: DepositOrWithdrawal): tx is Deposit { return tx.direction === 'deposit' } -async function transformTransaction(tx: Transfer): Promise { +export async function transformTransaction( + tx: Transfer +): Promise { // teleport-from-subgraph doesn't have a child-chain-id, we detect it later, hence, an early return if (isTransferTeleportFromSubgraph(tx)) { return await transformTeleportFromSubgraph(tx) @@ -235,7 +237,7 @@ function getCacheKeyFromTransaction( } // remove the duplicates from the transactions passed -function dedupeTransactions(txs: Transfer[]) { +export function dedupeTransactions(txs: Transfer[]) { return Array.from( new Map(txs.map(tx => [getCacheKeyFromTransaction(tx), tx])).values() ) diff --git a/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts b/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts index fc7fc23e03..3910be7fc9 100644 --- a/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts +++ b/packages/arb-token-bridge-ui/src/util/deposits/fetchDepositTxFromSubgraph.ts @@ -1,119 +1,48 @@ -import { getArbitrumNetwork, getArbitrumNetworks } from '@arbitrum/sdk' +import { getArbitrumNetwork } from '@arbitrum/sdk' import { Address } from 'wagmi' -import { FetchDepositsFromSubgraphResult } from './fetchDepositsFromSubgraph' -import { getAPIBaseUrl, sanitizeQueryParams } from '..' -import { hasL1Subgraph } from '../SubgraphUtils' -import { ChainId, getNetworkName } from '../networks' -import { fetchNativeCurrency } from '../../hooks/useNativeCurrency' -import { mapDepositsFromSubgraph } from './mapDepositsFromSubgraph' -import { Transaction } from '../../types/Transactions' +import { ChainId } from '../networks' import { getProviderForChainId } from '@/token-bridge-sdk/utils' +import { fetchDeposits } from './fetchDeposits' +import { transformTransaction } from '../../hooks/useTransactionHistory' +import { MergedTransaction } from '../../state/app/state' /** * Fetches initiated ETH deposit from event logs using transaction id * * @param txHash transaction id on parent chain */ -export async function fetchDepositTxFromSubgraph( +export async function fetchDepositByTxHash( txHash: string, connectedAddress: Address -): Promise { - // TODO: test with Arbitrum Sepolia - const supportedChildChains = [getArbitrumNetwork(ChainId.ArbitrumSepolia)] +): Promise { + // TODO: accept child chain id as an arg of the function + const childChainId = ChainId.ArbitrumSepolia + const childChain = getArbitrumNetwork(childChainId) - const fetcherList = supportedChildChains.map(childChain => { - const childChainId = childChain.chainId - - if (!hasL1Subgraph(Number(childChainId))) { - console.error( - `Parent chain subgraph not available for network: ${getNetworkName( - childChainId - )} (${childChainId})` - ) - return undefined - } - - const urlParams = new URLSearchParams( - sanitizeQueryParams({ - search: txHash, - l2ChainId: childChainId, - sender: connectedAddress - }) - ) - - return { - fetcher: fetch(`${getAPIBaseUrl()}/api/deposits?${urlParams}`, { - method: 'GET', - headers: { 'Content-Type': 'application/json' } - }), - parentChainId: childChain.parentChainId, - childChainId - } - }) + const parentProvider = getProviderForChainId(childChain.parentChainId) + const childProvider = getProviderForChainId(childChainId) try { - const responses = await Promise.all(fetcherList.map(item => item?.fetcher)) - - // there is always only one response because this tx only lives on one chain and no collision is possible - const results = responses - .map((response, index) => { - const fetcherItem = fetcherList[index] - if (typeof fetcherItem === 'undefined') { - return undefined - } - return { - response, - parentChainId: fetcherItem.parentChainId, - childChainId: fetcherItem.childChainId - } - }) - .filter(Boolean) as { - response: Response - parentChainId: number - childChainId: number - }[] - - type DepositFromSubgraph = { - data: FetchDepositsFromSubgraphResult[] - parentChainId: number - childChainId: number - } + const deposits = await fetchDeposits({ + sender: connectedAddress, + l1Provider: parentProvider, + l2Provider: childProvider, + searchString: txHash + }) + console.log('deposits: ', deposits) - const depositsFromSubgraph: DepositFromSubgraph[] = ( - await Promise.all(results.map(result => result.response.json())) + const transformedDeposits = await Promise.all( + deposits.map(transformTransaction) ) - .map((result, index) => ({ - data: result.data, - parentChainId: results[index]?.parentChainId, - childChainId: results[index]?.childChainId - })) - .filter( - (result): result is DepositFromSubgraph => - result.data.length > 0 && - typeof result.parentChainId !== 'undefined' && - typeof result.childChainId !== 'undefined' - ) - console.log('depositsFromSubgraph? ', depositsFromSubgraph) - - if (typeof depositsFromSubgraph[0] === 'undefined') { - return - } - - const nativeCurrency = await fetchNativeCurrency({ - provider: getProviderForChainId(depositsFromSubgraph[0].childChainId) - }) - const mappedDepositsFromSubgraph: Transaction[] = mapDepositsFromSubgraph({ - depositsFromSubgraph: depositsFromSubgraph[0].data, - nativeCurrency, - l1ChainId: depositsFromSubgraph[0].parentChainId, - l2ChainId: depositsFromSubgraph[0].childChainId - }) + console.log('transformedDeposits: ', transformedDeposits) - return mappedDepositsFromSubgraph + return transformedDeposits } catch (error) { - return undefined + console.error('Error fetching deposits by tx hash: ', error) } + + return [] } diff --git a/packages/arb-token-bridge-ui/src/util/deposits/fetchDeposits.ts b/packages/arb-token-bridge-ui/src/util/deposits/fetchDeposits.ts index 8953e44ae8..fe7b10613c 100644 --- a/packages/arb-token-bridge-ui/src/util/deposits/fetchDeposits.ts +++ b/packages/arb-token-bridge-ui/src/util/deposits/fetchDeposits.ts @@ -14,7 +14,7 @@ import { } from './fetchEthDepositsToCustomDestinationFromSubgraph' import { mapDepositsFromSubgraph } from './mapDepositsFromSubgraph' import { getParentToChildEventsByTxHash } from '../../components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash' -import { DepositInitiatedEvent } from '@arbitrum/sdk/dist/lib/abi/L1ERC20Gateway' +import { dedupeTransactions } from '../../hooks/useTransactionHistory' export type FetchDepositParams = { sender?: string @@ -42,9 +42,13 @@ export const fetchDeposits = async ({ pageNumber = 0, searchString = '' }: FetchDepositParams): Promise => { - if (typeof sender === 'undefined' && typeof receiver === 'undefined') - return [] - if (!l1Provider || !l2Provider) return [] + if (!searchString) { + if (typeof sender === 'undefined' && typeof receiver === 'undefined') { + return [] + } + + if (!l1Provider || !l2Provider) return [] + } const l1ChainId = (await l1Provider.getNetwork()).chainId const l2ChainId = (await l2Provider.getNetwork()).chainId @@ -130,10 +134,12 @@ export const fetchDeposits = async ({ filter: { fromBlock, toBlock: toBlock ?? 'latest' } }) - return [ - ...ethDepositsFromEventLogs, - ...tokenDepositsFromEventLogs, - ...mappedDepositsFromSubgraph, - ...mappedEthDepositsToCustomDestinationFromSubgraph - ].sort((a, b) => Number(b.timestampCreated) - Number(a.timestampCreated)) + return ( + dedupeTransactions([ + ...ethDepositsFromEventLogs, + ...tokenDepositsFromEventLogs, + ...mappedDepositsFromSubgraph, + ...mappedEthDepositsToCustomDestinationFromSubgraph + ]) as Transaction[] + ).sort((a, b) => Number(b.timestampCreated) - Number(a.timestampCreated)) } From 5bea6a63f681bf1de2c31bb55ba1dee016c37e20 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 24 Dec 2024 13:25:20 +0000 Subject: [PATCH 47/49] wip for token deposit event log --- .../getParentToChildEventsByTxHash.ts | 49 +++++++++---------- .../src/util/deposits/fetchDeposits.ts | 2 + 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts index 99d7eac792..02b8d3424e 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts @@ -235,22 +235,14 @@ export async function getParentToChildEventsByTxHash({ const tokenDepositRetryables: Transaction[] = await Promise.all( allMessages.tokenDepositRetryables .map(async tokenDepositMessage => { - const retryableReceipt = - await tokenDepositMessage.getRetryableCreationReceipt() - if (!retryableReceipt) { - return Promise.resolve([]) - } + const retryableTicketId = tokenDepositMessage.retryableCreationId + const childChainTx = + await tokenDepositMessage.childProvider.getTransaction( + retryableTicketId + ) const childChainId = await getChainIdFromProvider( tokenDepositMessage.childProvider ) - const childChain = getArbitrumNetwork(childChainId) - - const childChainTxReceipt = new ChildTransactionReceipt( - retryableReceipt - ) - const [event] = childChainTxReceipt.getChildToParentEvents() - - const receiver = await tokenDepositMessage.getBeneficiary() let parentChainErc20Address: string | undefined, tokenAmount: string | undefined, @@ -267,23 +259,22 @@ export async function getParentToChildEventsByTxHash({ | undefined try { - if (!event) { - return Promise.resolve([]) - } - const retryableMessageData = event.data + const childChain = getArbitrumNetwork(childChainId) + const retryableMessageData = childChainTx.data + // submitRetryable(bytes32,uint256,uint256,uint256,uint256,uint64,uint256,address,address,address,bytes) + // https://www.4byte.directory/signatures/?bytes4_signature=0xc9f95d32 const retryableBody = retryableMessageData.split('0xc9f95d32')[1] const requestId = '0x' + retryableBody?.slice(0, 64) - const depositsInitiatedLogs = parentTxReceipt.logs.filter( - log => - log.address.toLowerCase() === - childChain.ethBridge.inbox.toLowerCase() - ) + const depositsInitiatedLogs = await getDepositInitiatedLogs({ + fromBlock: parentTxReceipt.blockNumber, + toBlock: parentTxReceipt.blockNumber, + parentChainProvider: parentProvider, + childChain + }) const depositsInitiatedEvent = depositsInitiatedLogs.find( log => log.topics[3] === requestId ) - // @ts-ignore parentChainErc20Address = depositsInitiatedEvent?.event[0] - // @ts-ignore tokenAmount = depositsInitiatedEvent?.event[4]?.toString() } catch (e) { console.log(e) @@ -316,10 +307,16 @@ export async function getParentToChildEventsByTxHash({ } } + const receiver = childChainTx.to + + const timestamp = ( + await parentProvider.getBlock(parentTxReceipt.blockNumber) + ).timestamp + const depositTx: Transaction = { destination: receiver, sender: parentTxReceipt.from, - timestampCreated: event?.timestamp.toString(), + timestampCreated: timestamp.toString(), type: 'deposit', isClassic: false, txID: parentTxReceipt.transactionHash, @@ -335,7 +332,7 @@ export async function getParentToChildEventsByTxHash({ direction: 'deposit', source: 'event_logs', assetType: AssetType.ERC20, - assetName: tokenDepositData?.l1Token.name ?? '', + assetName: tokenDepositData?.l1Token.symbol ?? '', parentChainId, childChainId, l1NetworkID: parentChainId.toString(), diff --git a/packages/arb-token-bridge-ui/src/util/deposits/fetchDeposits.ts b/packages/arb-token-bridge-ui/src/util/deposits/fetchDeposits.ts index fe7b10613c..83a024b0db 100644 --- a/packages/arb-token-bridge-ui/src/util/deposits/fetchDeposits.ts +++ b/packages/arb-token-bridge-ui/src/util/deposits/fetchDeposits.ts @@ -134,6 +134,8 @@ export const fetchDeposits = async ({ filter: { fromBlock, toBlock: toBlock ?? 'latest' } }) + console.log('tokenDepositsFromEventLogs? ', tokenDepositsFromEventLogs) + return ( dedupeTransactions([ ...ethDepositsFromEventLogs, From 2c5b4cb1689f8fcfe52395399e8f7745eaa0cce9 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Tue, 24 Dec 2024 16:30:06 +0000 Subject: [PATCH 48/49] update --- .../getParentToChildEventsByTxHash.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts index 02b8d3424e..16480640bb 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts @@ -240,6 +240,10 @@ export async function getParentToChildEventsByTxHash({ await tokenDepositMessage.childProvider.getTransaction( retryableTicketId ) + const childChainTxReceipt = + await tokenDepositMessage.childProvider.getTransactionReceipt( + retryableTicketId + ) const childChainId = await getChainIdFromProvider( tokenDepositMessage.childProvider ) @@ -307,7 +311,8 @@ export async function getParentToChildEventsByTxHash({ } } - const receiver = childChainTx.to + // TODO: this address is wrong, whyyyyy + const receiver = tokenDepositMessage.messageData.destAddress const timestamp = ( await parentProvider.getBlock(parentTxReceipt.blockNumber) @@ -333,6 +338,7 @@ export async function getParentToChildEventsByTxHash({ source: 'event_logs', assetType: AssetType.ERC20, assetName: tokenDepositData?.l1Token.symbol ?? '', + tokenAddress: parentChainErc20Address, parentChainId, childChainId, l1NetworkID: parentChainId.toString(), From 6af7e9fde4fa89f633b8909e36bbe94b82e4d165 Mon Sep 17 00:00:00 2001 From: Fionna <13184582+fionnachan@users.noreply.github.com> Date: Fri, 27 Dec 2024 15:10:47 +0000 Subject: [PATCH 49/49] clean up get token deposits from event log a bit --- .../getParentToChildEventsByTxHash.ts | 188 +++++------------- ...ParentToChildMessagesAndDepositMessages.ts | 109 ---------- .../TransactionHistoryTxHashSearch/helpers.ts | 8 + .../util/deposits/mapDepositsFromSubgraph.ts | 2 +- 4 files changed, 63 insertions(+), 244 deletions(-) delete mode 100644 packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages.ts diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts index 16480640bb..7fd7e63514 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildEventsByTxHash.ts @@ -1,16 +1,9 @@ import { BigNumber, providers, utils } from 'ethers' -import { BlockTag, Provider } from '@ethersproject/providers' -import { - DepositInitiatedEvent, - L1ERC20Gateway -} from '@arbitrum/sdk/dist/lib/abi/L1ERC20Gateway' +import { BlockTag } from '@ethersproject/providers' import { L1ERC20Gateway__factory } from '@arbitrum/sdk/dist/lib/abi/factories/L1ERC20Gateway__factory' import { ERC20__factory } from '@arbitrum/sdk/dist/lib/abi/factories/ERC20__factory' import { ArbitrumNetwork, - ChildTransactionReceipt, - EventFetcher, - getArbitrumNetwork, getArbitrumNetworks, ParentToChildMessageReader, ParentToChildMessageReaderClassic, @@ -27,6 +20,7 @@ import { Transaction } from '../../../types/Transactions' import { updateAdditionalDepositData } from '../../../util/deposits/helpers' import { AssetType } from '../../../hooks/arbTokenBridge.types' import { fetchNativeCurrency } from '../../../hooks/useNativeCurrency' +import { parseTypedLogs } from '@arbitrum/sdk/dist/lib/dataEntities/event' export type FetchDepositTxFromEventLogResult = { receiver: string @@ -48,60 +42,8 @@ export type FetchDepositTxFromEventLogResult = { } } -const getDepositInitiatedLogs = async ({ - fromBlock, - toBlock, - parentChainProvider, - childChain -}: { - fromBlock: number - toBlock: number - parentChainProvider: Provider - childChain: ArbitrumNetwork -}) => { - const [ - depositsInitiatedLogsL1Erc20Gateway, - depositsInitiatedLogsL1CustomGateway, - depositsInitiatedLogsL1WethGateway - ] = await Promise.all( - [ - childChain.tokenBridge!.parentErc20Gateway, - childChain.tokenBridge!.parentCustomGateway, - childChain.tokenBridge!.parentWethGateway - ].map(gatewayAddress => { - return getDepositInitiatedEventData( - gatewayAddress, - { fromBlock, toBlock }, - parentChainProvider - ) - }) - ) - - return [ - ...(depositsInitiatedLogsL1Erc20Gateway || []), - ...(depositsInitiatedLogsL1CustomGateway || []), - ...(depositsInitiatedLogsL1WethGateway || []) - ] -} - -const getDepositInitiatedEventData = async ( - parentChainGatewayAddress: string, - filter: { - fromBlock: providers.BlockTag - toBlock: providers.BlockTag - }, - parentChainProvider: providers.Provider -) => { - const eventFetcher = new EventFetcher(parentChainProvider) - const logs = await eventFetcher.getEvents< - L1ERC20Gateway, - DepositInitiatedEvent - >(L1ERC20Gateway__factory, (g: any) => g.filters.DepositInitiated(), { - ...filter, - address: parentChainGatewayAddress - }) - - return logs +const getDepositInitiatedLogs = (logs: providers.Log[]) => { + return parseTypedLogs(L1ERC20Gateway__factory, logs, 'DepositInitiated') } async function getChildChainMessages( @@ -152,7 +94,6 @@ async function getChildChainMessages( * @param hash The uniqueId indexed field was removed in nitro and a hash indexed field was added. * For pre-nitro events the value passed in here will be used to find events with the same uniqueId. * For post nitro events it will be used to find events with the same hash. - * @param indexInBatch The index in the batch, only valid for pre-nitro events. This parameter is ignored post-nitro * @returns Any classic and nitro events that match the provided filters. */ export async function getParentToChildEventsByTxHash({ @@ -160,15 +101,13 @@ export async function getParentToChildEventsByTxHash({ filter, position, destination, - txHash, - indexInBatch + txHash }: { parentChainId: number filter?: { fromBlock: BlockTag; toBlock: BlockTag } position?: BigNumber destination?: string txHash?: string - indexInBatch?: BigNumber }): Promise<{ ethDeposits: Transaction[] tokenDepositRetryables: Transaction[] @@ -200,7 +139,7 @@ export async function getParentToChildEventsByTxHash({ return { ethDeposits: [], tokenDepositRetryables: [] } } - const allMessages: ParentToChildMessagesAndDepositMessages = messages + const allMessages = messages .filter(message => typeof message !== 'undefined') .reduce( (acc, value) => { @@ -229,97 +168,78 @@ export async function getParentToChildEventsByTxHash({ tokenDepositRetryables: [], tokenDepositRetryablesClassic: [], ethDeposits: [] - } as ParentToChildMessagesAndDepositMessages - ) + } + // eslint complains that it can be undefined if not cast here + ) as ParentToChildMessagesAndDepositMessages const tokenDepositRetryables: Transaction[] = await Promise.all( allMessages.tokenDepositRetryables .map(async tokenDepositMessage => { - const retryableTicketId = tokenDepositMessage.retryableCreationId - const childChainTx = - await tokenDepositMessage.childProvider.getTransaction( - retryableTicketId - ) - const childChainTxReceipt = - await tokenDepositMessage.childProvider.getTransactionReceipt( - retryableTicketId - ) const childChainId = await getChainIdFromProvider( tokenDepositMessage.childProvider ) - let parentChainErc20Address: string | undefined, - tokenAmount: string | undefined, - tokenDepositData: - | { - tokenAmount: string | undefined - l1Token: { - symbol: string - decimals: number - id: string - name: string - } - } - | undefined + const depositsInitiatedLogs = getDepositInitiatedLogs( + parentTxReceipt.logs + ) - try { - const childChain = getArbitrumNetwork(childChainId) - const retryableMessageData = childChainTx.data - // submitRetryable(bytes32,uint256,uint256,uint256,uint256,uint64,uint256,address,address,address,bytes) - // https://www.4byte.directory/signatures/?bytes4_signature=0xc9f95d32 - const retryableBody = retryableMessageData.split('0xc9f95d32')[1] - const requestId = '0x' + retryableBody?.slice(0, 64) - const depositsInitiatedLogs = await getDepositInitiatedLogs({ - fromBlock: parentTxReceipt.blockNumber, - toBlock: parentTxReceipt.blockNumber, - parentChainProvider: parentProvider, - childChain - }) - const depositsInitiatedEvent = depositsInitiatedLogs.find( - log => log.topics[3] === requestId - ) - parentChainErc20Address = depositsInitiatedEvent?.event[0] - tokenAmount = depositsInitiatedEvent?.event[4]?.toString() - } catch (e) { - console.log(e) + if (typeof depositsInitiatedLogs === 'undefined') { + return } - const parentChainProvider = getProviderForChainId(parentChainId) + const firstDepositInitiatedLog = depositsInitiatedLogs[0] + if (typeof firstDepositInitiatedLog === 'undefined') { + return + } + + const parentChainErc20Address = firstDepositInitiatedLog.l1Token + const tokenAmount = firstDepositInitiatedLog._amount.toString() - if (parentChainErc20Address) { - try { - const erc20 = ERC20__factory.connect( - parentChainErc20Address, - parentChainProvider - ) - const [name, symbol, decimals] = await Promise.all([ - erc20.name(), - erc20.symbol(), - erc20.decimals() - ]) - tokenDepositData = { - tokenAmount, + let tokenDepositData: + | { + tokenAmount: string | undefined l1Token: { - symbol, - decimals, - id: parentChainErc20Address, - name + symbol: string + decimals: number + id: string + name: string } } - } catch (e) { - console.log('failed to fetch token data', e) + | undefined + + const parentChainProvider = getProviderForChainId(parentChainId) + + try { + const erc20 = ERC20__factory.connect( + parentChainErc20Address, + parentChainProvider + ) + const [name, symbol, decimals] = await Promise.all([ + erc20.name(), + erc20.symbol(), + erc20.decimals() + ]) + tokenDepositData = { + tokenAmount, + l1Token: { + symbol, + decimals, + id: parentChainErc20Address, + name + } } + } catch (e) { + console.log('failed to fetch token data', e) } - // TODO: this address is wrong, whyyyyy - const receiver = tokenDepositMessage.messageData.destAddress + const receiverFromLog = firstDepositInitiatedLog._to const timestamp = ( await parentProvider.getBlock(parentTxReceipt.blockNumber) ).timestamp const depositTx: Transaction = { - destination: receiver, + destination: receiverFromLog, sender: parentTxReceipt.from, timestampCreated: timestamp.toString(), type: 'deposit', diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages.ts deleted file mode 100644 index 671f967562..0000000000 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/getParentToChildMessagesAndDepositMessages.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { - getArbitrumNetwork, - getArbitrumNetworks, - ParentTransactionReceipt -} from '@arbitrum/sdk' - -import { - EthDepositMessageWithNetwork, - ParentToChildMessagesAndDepositMessages -} from './helpers' -import { getProviderForChainId } from '@/token-bridge-sdk/utils' - -export const getParentToChildMessagesAndDepositMessages = async ( - parentTxReceipt: ParentTransactionReceipt, - parentChainId: number -): Promise => { - try { - const childNetworks = getArbitrumNetworks() - .filter(childChain => childChain.parentChainId === parentChainId) - .map(network => network.chainId) - const messagesPromises = childNetworks.map(async childChainId => { - // TODO: error handle - const childNetwork = await getArbitrumNetwork(childChainId) - - // Check if any l1ToL2 msg is sent to the inbox of this l2Network - const logFromL2Inbox = parentTxReceipt.logs.filter(log => { - return ( - log.address.toLowerCase() === - childNetwork.ethBridge.inbox.toLowerCase() - ) - }) - if (logFromL2Inbox.length === 0) { - return - } - - const childProvider = getProviderForChainId(childChainId) - const isClassic = await parentTxReceipt.isClassic(childProvider) - - if (isClassic) { - const messages = ( - await parentTxReceipt.getParentToChildMessagesClassic(childProvider) - ).map(l1ToL2Message => { - return Object.assign(l1ToL2Message, { childNetwork }) - }) - return { - allL1ToL2Messages: [], - allL1ToL2MessagesClassic: messages, - allDepositMessages: [], - childChainId - } - } else { - const messages = ( - await parentTxReceipt.getParentToChildMessages(childProvider) - ).map(l1ToL2Message => { - return Object.assign(l1ToL2Message, { childNetwork }) - }) - - const depositMessagesWithNetwork: EthDepositMessageWithNetwork[] = ( - await parentTxReceipt.getEthDeposits(childProvider) - ).map(depositMessage => { - return Object.assign(depositMessage, { childNetwork }) - }) - - return { - allL1ToL2Messages: messages, - allDepositMessages: depositMessagesWithNetwork, - allL1ToL2MessagesClassic: [], - childChainId - } - } - }) - - const messages = await Promise.all(messagesPromises) - - const allMessages = messages.reduce( - (acc, value) => { - if (!value) { - return acc - } - return { - retryables: acc.retryables.concat(value.allL1ToL2Messages), - retryablesClassic: acc.retryablesClassic.concat( - value.allL1ToL2MessagesClassic - ), - deposits: acc.deposits.concat(value.allDepositMessages), - childChainId: value.childChainId - } - }, - { - retryables: [], - retryablesClassic: [], - deposits: [], - childChainId: null - } as ParentToChildMessagesAndDepositMessages - ) - return allMessages - } catch (error) { - console.error( - 'Error in getParentToChildMessagesAndDepositMessages: ', - error - ) - return { - retryables: [], - retryablesClassic: [], - deposits: [], - childChainId: null - } as ParentToChildMessagesAndDepositMessages - } -} diff --git a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts index b44182dbdd..c6886cd96c 100644 --- a/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts +++ b/packages/arb-token-bridge-ui/src/components/TransactionHistory/TransactionHistoryTxHashSearch/helpers.ts @@ -34,6 +34,14 @@ export enum ReceiptState { NO_L2_L1_MESSAGES } +export type TokenDepositEvent = { + l1Token: string + _from: string + _to: string + _sequenceNumber: BigNumber + _amount: BigNumber +} + export interface ParentToChildMessagesAndDepositMessages { tokenDepositRetryables: ParentToChildMessageReader[] tokenDepositRetryablesClassic: ParentToChildMessageReaderClassic[] diff --git a/packages/arb-token-bridge-ui/src/util/deposits/mapDepositsFromSubgraph.ts b/packages/arb-token-bridge-ui/src/util/deposits/mapDepositsFromSubgraph.ts index 45b0b531a0..50adda01de 100644 --- a/packages/arb-token-bridge-ui/src/util/deposits/mapDepositsFromSubgraph.ts +++ b/packages/arb-token-bridge-ui/src/util/deposits/mapDepositsFromSubgraph.ts @@ -3,7 +3,7 @@ import { defaultErc20Decimals } from '../../defaults' import { AssetType } from '../../hooks/arbTokenBridge.types' import { NativeCurrency } from '../../hooks/useNativeCurrency' import { FetchDepositsFromSubgraphResult } from './fetchDepositsFromSubgraph' -import { Transaction } from '../../hooks/useTransactions' +import { Transaction } from '../../types/Transactions' export function mapDepositsFromSubgraph({ depositsFromSubgraph,