Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: new tx history - cache tx details #1371

Merged
merged 94 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from 87 commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
be0e9e0
cctp
brtkx Oct 26, 2023
64369cf
Merge branch 'new-tx-history-old-ui' into new-tx-history-cctp
brtkx Oct 27, 2023
2c4ad7b
Merge branch 'new-tx-history-old-ui' into new-tx-history-cctp
brtkx Oct 27, 2023
c4a6df9
Merge branch 'new-tx-history-old-ui' into new-tx-history-cctp
brtkx Oct 31, 2023
188be70
infinite scrolling
brtkx Oct 31, 2023
e73ba69
Merge branch 'new-tx-history-old-ui' into new-tx-history-cctp
brtkx Oct 31, 2023
f2225bf
Merge branch 'new-tx-history-cctp' into new-tx-history-ui-pagination
brtkx Oct 31, 2023
080affe
Merge branch 'new-tx-history-old-ui' of github.com:OffchainLabs/arbit…
brtkx Dec 4, 2023
67f4c5d
fixes
brtkx Dec 4, 2023
6da99ec
Merge branch 'new-tx-history-old-ui' into new-tx-history-cctp
brtkx Dec 4, 2023
8543bef
Merge branch 'new-tx-history-old-ui' into new-tx-history-cctp
brtkx Dec 4, 2023
5278afc
Merge branch 'new-tx-history-old-ui' into new-tx-history-cctp
brtkx Dec 4, 2023
47c0ba9
Merge branch 'new-tx-history-cctp' of github.com:OffchainLabs/arbitru…
brtkx Dec 5, 2023
3228452
clean up
brtkx Dec 5, 2023
43fb44e
fixes
brtkx Dec 5, 2023
6c46790
remove logs
brtkx Dec 6, 2023
2f72b6c
fixes
brtkx Dec 6, 2023
f2dc99e
fix
brtkx Dec 6, 2023
99ec664
fix
brtkx Dec 6, 2023
316391b
fetch by ts
brtkx Dec 6, 2023
8ae47c2
Merge branch 'new-tx-history-cctp' of github.com:OffchainLabs/arbitru…
brtkx Dec 6, 2023
52b9d25
clean up
brtkx Dec 6, 2023
070df16
fix
brtkx Dec 6, 2023
4379fa4
space
brtkx Dec 6, 2023
19decb3
add logs
brtkx Dec 6, 2023
08f40da
fix
brtkx Dec 6, 2023
672e31c
fix
brtkx Dec 6, 2023
7ccac83
only show load more if not completed
brtkx Dec 6, 2023
b4dbcb2
tx history includer
brtkx Dec 7, 2023
d2772c0
unused state
brtkx Dec 7, 2023
fdd545b
display dest network if L1 tx pending
brtkx Dec 7, 2023
ebe5c86
merge
brtkx Dec 7, 2023
cd06852
not needed classname
brtkx Dec 7, 2023
809ddb2
add comments to the pausing logic
brtkx Dec 7, 2023
51a7794
comment typo
brtkx Dec 7, 2023
68a6fba
init
brtkx Dec 7, 2023
5e8bd99
clean up
brtkx Dec 7, 2023
97360ab
updaters
brtkx Dec 8, 2023
785ce95
swr infinite
brtkx Dec 8, 2023
7df1356
naming
brtkx Dec 8, 2023
c44f665
clean up
brtkx Dec 8, 2023
58c06c7
Merge branch 'new-tx-history-ui-pagination' of github.com:OffchainLab…
brtkx Dec 8, 2023
95bb24d
fix
brtkx Dec 11, 2023
036fa5a
Merge branch 'new-tx-history-ui-pagination' of github.com:OffchainLab…
brtkx Dec 11, 2023
03047bb
fixes after swr infinite
brtkx Dec 11, 2023
065f498
Merge branch 'new-tx-history-tx-includer' of github.com:OffchainLabs/…
brtkx Dec 11, 2023
71a5416
fixes
brtkx Dec 11, 2023
6b13f36
fix tx list
brtkx Dec 11, 2023
6070b51
fix and clean up
brtkx Dec 11, 2023
2c6244a
based on days
brtkx Dec 12, 2023
bd9a7c2
based on days only
brtkx Dec 12, 2023
ee0fad1
Merge branch 'new-tx-history-ui-pagination' of github.com:OffchainLab…
brtkx Dec 12, 2023
3c69e43
Merge branch 'new-tx-history-tx-includer' of github.com:OffchainLabs/…
brtkx Dec 12, 2023
d4fe7b7
fix
brtkx Dec 12, 2023
e2be948
separate swr for new txs
brtkx Dec 12, 2023
952bb2e
remove unused param
brtkx Dec 12, 2023
7809856
address review comments
brtkx Dec 12, 2023
ceb555b
address comment
brtkx Dec 12, 2023
b350668
nit
brtkx Dec 12, 2023
4133320
fixes and improvements
brtkx Dec 13, 2023
5ca9b1f
fix
brtkx Dec 13, 2023
d20fe79
claiming
brtkx Dec 14, 2023
e2cec54
claiming clean up
brtkx Dec 14, 2023
f6bf775
claiming clean up
brtkx Dec 14, 2023
fb93b23
updates
brtkx Dec 14, 2023
237455d
fixes
brtkx Dec 14, 2023
1307f5f
testnet mode
brtkx Dec 14, 2023
40e554e
custom orbit chains
brtkx Dec 14, 2023
3e57e91
token timestamps
brtkx Dec 14, 2023
9d1d5de
custom orbit chains
brtkx Dec 14, 2023
842b5dc
Merge branch 'new-tx-history-testnet-mode' of github.com:OffchainLabs…
brtkx Dec 14, 2023
24fb120
ui fixes
brtkx Dec 15, 2023
589da16
fix
brtkx Dec 15, 2023
b254b73
fix
brtkx Dec 15, 2023
f910b8c
caching txs
brtkx Dec 18, 2023
162a420
Merge branch 'new-tx-history-old-ui' of github.com:OffchainLabs/arbit…
brtkx Dec 20, 2023
11812a9
fix
brtkx Dec 20, 2023
6fb25d8
comment out sepolia, fixes
brtkx Dec 20, 2023
56de752
fix
brtkx Dec 20, 2023
e30ce4d
clean up
brtkx Dec 20, 2023
dd28b66
Merge branch 'new-tx-history-ui-fixes' of github.com:OffchainLabs/arb…
brtkx Dec 20, 2023
bdb7746
clean up
brtkx Dec 20, 2023
0b6a2cd
Merge branch 'new-tx-history-old-ui' of github.com:OffchainLabs/arbit…
brtkx Dec 20, 2023
c95a523
add nonce
brtkx Dec 20, 2023
09e3e80
changes
brtkx Dec 20, 2023
8a2b2e1
fixes
brtkx Dec 20, 2023
fb6347e
fix
brtkx Dec 20, 2023
1e5c043
address comments
brtkx Dec 21, 2023
85e745a
testing
brtkx Dec 21, 2023
43ca07a
try
brtkx Dec 21, 2023
ffedd37
try
brtkx Dec 21, 2023
3875249
fix
brtkx Dec 21, 2023
b8903d2
revert
brtkx Dec 21, 2023
9acba66
fix
brtkx Dec 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ import {
isNetwork
} from '../../util/networks'
import { InformationCircleIcon } from '@heroicons/react/24/outline'
import {
isCustomDestinationAddressTx,
findMatchingL1TxForWithdrawal,
isPending
} from '../../state/app/utils'
import { isCustomDestinationAddressTx, isPending } from '../../state/app/utils'
import { TokenIcon, TransactionDateTime } from './TransactionHistoryTable'
import { formatAmount } from '../../util/NumberUtils'
import { sanitizeTokenSymbol } from '../../util/TokenUtils'
Expand All @@ -29,6 +25,7 @@ import { useRemainingTime } from '../../state/cctpState'
import { useChainLayers } from '../../hooks/useChainLayers'
import { getWagmiChain } from '../../util/wagmi/getWagmiChain'
import { NetworkImage } from '../common/NetworkImage'
import { getWithdrawalClaimParentChainTxDetails } from './helpers'

type CommonProps = {
tx: MergedTransaction
Expand All @@ -37,9 +34,9 @@ type CommonProps = {

function ClaimableRowStatus({ tx }: CommonProps) {
const { parentLayer, layer } = useChainLayers()
const matchingL1Tx = tx.isCctp
const matchingL1TxId = tx.isCctp
? tx.cctpData?.receiveMessageTransactionHash
: findMatchingL1TxForWithdrawal(tx)
: getWithdrawalClaimParentChainTxDetails(tx)?.txId

switch (tx.status) {
case 'pending':
Expand Down Expand Up @@ -96,7 +93,7 @@ function ClaimableRowStatus({ tx }: CommonProps) {
)

case 'Executed': {
if (typeof matchingL1Tx === 'undefined') {
if (typeof matchingL1TxId === 'undefined') {
return (
<div className="flex flex-col space-y-1">
<StatusBadge
Expand Down Expand Up @@ -187,13 +184,11 @@ function ClaimableRowTime({ tx }: CommonProps) {
)
}

const claimedTx = tx.isCctp
? {
createdAt: tx.cctpData?.receiveMessageTimestamp
}
: findMatchingL1TxForWithdrawal(tx)
const claimedTxTimestamp = tx.isCctp
? tx.cctpData?.receiveMessageTimestamp
: getWithdrawalClaimParentChainTxDetails(tx)?.timestamp

if (typeof claimedTx === 'undefined') {
if (typeof claimedTxTimestamp === 'undefined') {
return (
<div className="flex flex-col space-y-3">
<Tooltip content={<span>{layer} Transaction time</span>}>
Expand All @@ -213,10 +208,10 @@ function ClaimableRowTime({ tx }: CommonProps) {
<Tooltip content={<span>{layer} Transaction Time</span>}>
<TransactionDateTime standardizedDate={tx.createdAt} />
</Tooltip>
{claimedTx?.createdAt && (
{claimedTxTimestamp && (
<Tooltip content={<span>{parentLayer} Transaction Time</span>}>
<span className="whitespace-nowrap">
<TransactionDateTime standardizedDate={claimedTx?.createdAt} />
<TransactionDateTime standardizedDate={claimedTxTimestamp} />
</span>
</Tooltip>
)}
Expand All @@ -231,13 +226,11 @@ function ClaimedTxInfo({ tx, isSourceChainArbitrum }: CommonProps) {
const isExecuted = tx.status === 'Executed'
const isBeingClaimed = tx.status === 'Confirmed' && tx.resolvedAt

const claimedTx = tx.isCctp
? {
txId: tx.cctpData?.receiveMessageTransactionHash
}
: findMatchingL1TxForWithdrawal(tx)
const claimedTxId = tx.isCctp
? tx.cctpData?.receiveMessageTransactionHash
: getWithdrawalClaimParentChainTxDetails(tx)?.txId

if (!claimedTx?.txId) {
if (!claimedTxId) {
return (
<span className="flex flex-nowrap items-center gap-1 whitespace-nowrap text-dark">
<span className="w-8 rounded-md pr-2 text-xs text-dark">To</span>
Expand All @@ -257,10 +250,10 @@ function ClaimedTxInfo({ tx, isSourceChainArbitrum }: CommonProps) {
<NetworkImage chainId={toNetworkId} />
{getNetworkName(toNetworkId)}:{' '}
<ExternalLink
href={`${getExplorerUrl(toNetworkId)}/tx/${claimedTx.txId}`}
href={`${getExplorerUrl(toNetworkId)}/tx/${claimedTxId}`}
className="arb-hover text-blue-link"
>
{shortenTxHash(claimedTx.txId)}
{shortenTxHash(claimedTxId)}
</ExternalLink>
</span>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,22 @@ import {
WithdrawalStatus
} from '../../state/app/state'
import { ChainId, getBlockTime, isNetwork, rpcURLs } from '../../util/networks'
import { isCctpTransfer } from '../../hooks/useTransactionHistory'
import {
ChainPair,
Deposit,
isCctpTransfer
} from '../../hooks/useTransactionHistory'
import { getWagmiChain } from '../../util/wagmi/getWagmiChain'
import { getL1ToL2MessageDataFromL1TxHash } from '../../util/deposits/helpers'
import { AssetType } from '../../hooks/arbTokenBridge.types'
import { getDepositStatus } from '../../state/app/utils'
import { getBlockBeforeConfirmation } from '../../state/cctpState'
import { getAttestationHashAndMessageFromReceipt } from '../../util/cctp/getAttestationHashAndMessageFromReceipt'

const CLAIM_PARENT_CHAIN_DETAILS_LOCAL_STORAGE_KEY =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Can we name it better? Wasn't clear at first what it meant 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like PARENT_CHAIN_TX_DETAILS_OF_CLAIM_TX ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

'arbitrum:bridge:claim:parent:tx:details'
const DEPOSITS_LOCAL_STORAGE_KEY = 'arbitrum:bridge:deposits'

export enum StatusLabel {
PENDING = 'Pending',
CLAIMABLE = 'Claimable',
Expand Down Expand Up @@ -101,13 +109,21 @@ export function getProvider(chainId: ChainId) {
}

export function isSameTransaction(
tx_1: MergedTransaction,
tx_2: MergedTransaction
txDetails_1: {
txId: string
parentChainId: ChainId
childChainId: ChainId
},
txDetails_2: {
txId: string
parentChainId: ChainId
childChainId: ChainId
}
) {
return (
tx_1.txId === tx_2.txId &&
tx_1.parentChainId === tx_2.parentChainId &&
tx_1.childChainId === tx_2.childChainId
txDetails_1.txId === txDetails_2.txId &&
txDetails_1.parentChainId === txDetails_2.parentChainId &&
txDetails_1.childChainId === txDetails_2.childChainId
)
}

Expand All @@ -133,6 +149,99 @@ function getWithdrawalStatusFromReceipt(
}
}

export function getDepositsWithoutStatusesFromCache(): Deposit[] {
return JSON.parse(
localStorage.getItem(DEPOSITS_LOCAL_STORAGE_KEY) ?? '[]'
) as Deposit[]
}

/**
* Cache deposits from event logs. We don't fetch these so we need to store them locally.
*
* @param {MergedTransaction} tx - Deposit from event logs to be cached.
*/
export function addDepositToCache(tx: Deposit) {
if (tx.direction !== 'deposit') {
return
}

const cachedDeposits = getDepositsWithoutStatusesFromCache()

const foundInCache = cachedDeposits.find(cachedTx =>
isSameTransaction(
{ ...cachedTx, txId: cachedTx.txID },
{ ...tx, txId: tx.txID }
)
)

if (foundInCache) {
return
}

const newCachedDeposits = [tx, ...cachedDeposits]

localStorage.setItem(
DEPOSITS_LOCAL_STORAGE_KEY,
JSON.stringify(newCachedDeposits)
)
}

/**
* Cache parent chain tx details when claiming. This is the chain the funds were claimed on. We store locally because we don't have access to this tx from the child chain tx data.
*
* @param {MergedTransaction} tx - Transaction that initiated the withdrawal (child chain transaction).
* @param {string} parentChainTxId - Transaction ID of the claim transaction (parent chain transaction ID).
*/
export function setWithdrawalClaimParentChainTxDetails(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: setParentChainTxDetailsOfWithdrawalClaimTx and similarly in other function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

tx: MergedTransaction,
parentChainTxId: string
) {
const key = `${tx.parentChainId}-${tx.childChainId}-${tx.txId}`

const cachedClaimParentChainTxId = JSON.parse(
localStorage.getItem(CLAIM_PARENT_CHAIN_DETAILS_LOCAL_STORAGE_KEY) ?? '{}'
)

if (key in cachedClaimParentChainTxId) {
// already set
return
}

localStorage.setItem(
CLAIM_PARENT_CHAIN_DETAILS_LOCAL_STORAGE_KEY,
JSON.stringify({
...cachedClaimParentChainTxId,
[key]: {
txId: parentChainTxId,
timestamp: dayjs().valueOf()
}
})
)
}

export function getWithdrawalClaimParentChainTxDetails(
tx: MergedTransaction
): { txId: string; timestamp: number } | undefined {
if (!tx.isWithdrawal || tx.isCctp) {
return undefined
}

const key = `${tx.parentChainId}-${tx.childChainId}-${tx.txId}`

const cachedClaimParentChainTxDetails = (
JSON.parse(
localStorage.getItem(CLAIM_PARENT_CHAIN_DETAILS_LOCAL_STORAGE_KEY) ?? '{}'
) as {
[key in string]: {
txId: string
timestamp: number
}
}
)[key]

return cachedClaimParentChainTxDetails
}

export async function getUpdatedEthDeposit(
tx: MergedTransaction
): Promise<MergedTransaction> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export type WithdrawalInitiated = EventArgs<WithdrawalInitiatedEvent> & {
txHash: string
timestamp?: BigNumber
direction: 'deposit' | 'withdrawal'
source: 'subgraph' | 'event_logs'
source: 'subgraph' | 'event_logs' | 'local_storage_cache'
parentChainId: number
childChainId: number
}
Expand Down
49 changes: 47 additions & 2 deletions packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { useUpdateUSDCBalances } from './CCTP/useUpdateUSDCBalances'
import { useNativeCurrency } from './useNativeCurrency'
import { useTransactionHistory } from './useTransactionHistory'
import { DepositStatus, WithdrawalStatus } from '../state/app/state'
import { addDepositToCache } from '../components/TransactionHistory/helpers'

export const wait = (ms = 0) => {
return new Promise(res => setTimeout(res, ms))
Expand Down Expand Up @@ -188,6 +189,9 @@ export const useArbTokenBridge = (
}

const ethBridger = await EthBridger.fromProvider(l2.provider)
const parentChainBlockTimestamp = Math.floor(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is Math.floor needed here since it will never be a decimal

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm I was getting a decimal value for some reason before but might have been a mistake on my part, removing math floor

(await l1.provider.getBlock('latest')).timestamp
)

let tx: L1EthDepositTransaction

Expand All @@ -212,7 +216,7 @@ export const useArbTokenBridge = (
destination: walletAddress,
direction: 'deposit-l1',
status: 'pending',
createdAt: dayjs().valueOf(),
createdAt: parentChainBlockTimestamp * 1_000,
resolvedAt: null,
txId: tx.hash,
asset: nativeCurrency.symbol,
Expand All @@ -227,6 +231,25 @@ export const useArbTokenBridge = (
childChainId: Number(l2NetworkID)
})

addDepositToCache({
sender: walletAddress,
destination: walletAddress,
status: 'pending',
txID: tx.hash,
assetName: nativeCurrency.symbol,
assetType: AssetType.ETH,
l1NetworkID,
l2NetworkID,
value: utils.formatUnits(amount, nativeCurrency.decimals),
parentChainId: Number(l1NetworkID),
childChainId: Number(l2NetworkID),
direction: 'deposit',
type: 'deposit-l1',
source: 'local_storage_cache',
timestampCreated: String(parentChainBlockTimestamp),
nonce: tx.nonce
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requested by @fionnachan, adding nonce so that we can fix #986 in the future

})

const receipt = await tx.wait()

if (txLifecycle?.onTxConfirm) {
Expand Down Expand Up @@ -398,6 +421,9 @@ export const useArbTokenBridge = (
return
}
const erc20Bridger = await Erc20Bridger.fromProvider(l2.provider)
const parentChainBlockTimestamp = Math.floor(
(await l1.provider.getBlock('latest')).timestamp
)

try {
const { symbol, decimals } = await fetchErc20Data({
Expand Down Expand Up @@ -428,7 +454,7 @@ export const useArbTokenBridge = (
destination: destinationAddress ?? walletAddress,
direction: 'deposit-l1',
status: 'pending',
createdAt: dayjs().valueOf(),
createdAt: parentChainBlockTimestamp * 1_000,
resolvedAt: null,
txId: tx.hash,
asset: symbol,
Expand All @@ -443,6 +469,25 @@ export const useArbTokenBridge = (
childChainId: Number(l2NetworkID)
})

addDepositToCache({
sender: walletAddress,
destination: destinationAddress ?? walletAddress,
status: 'pending',
txID: tx.hash,
assetName: symbol,
assetType: AssetType.ERC20,
l1NetworkID,
l2NetworkID,
fionnachan marked this conversation as resolved.
Show resolved Hide resolved
value: utils.formatUnits(amount, decimals),
parentChainId: Number(l1NetworkID),
childChainId: Number(l2NetworkID),
direction: 'deposit',
type: 'deposit-l1',
source: 'local_storage_cache',
timestampCreated: String(parentChainBlockTimestamp),
nonce: tx.nonce
})

const receipt = await tx.wait()

if (txLifecycle?.onTxConfirm) {
Expand Down
10 changes: 9 additions & 1 deletion packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import { MergedTransaction, WithdrawalStatus } from '../state/app/state'
import { isUserRejectedError } from '../util/isUserRejectedError'
import { errorToast } from '../components/common/atoms/Toast'
import { AssetType, L2ToL1EventResultPlus } from './arbTokenBridge.types'
import { getProvider } from '../components/TransactionHistory/helpers'
import {
getProvider,
setWithdrawalClaimParentChainTxDetails
} from '../components/TransactionHistory/helpers'
import { L2TransactionReceipt } from '@arbitrum/sdk'
import { ContractReceipt, utils } from 'ethers'
import { useTransactionHistory } from './useTransactionHistory'
Expand Down Expand Up @@ -109,12 +112,17 @@ export function useClaimWithdrawal(): UseClaimWithdrawalResult {
}

const isSuccess = (res as ContractReceipt).status === 1
const txHash = (res as ContractReceipt).transactionHash
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read timestamp from here


updatePendingTransaction({
...tx,
status: isSuccess ? WithdrawalStatus.EXECUTED : WithdrawalStatus.FAILURE,
resolvedAt: isSuccess ? dayjs().valueOf() : null
})

if (isSuccess) {
setWithdrawalClaimParentChainTxDetails(tx, txHash)
}
}

return { claim, isClaiming }
Expand Down
Loading