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

fix: fetch correct cctp history for contract wallets #2058

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
65 changes: 13 additions & 52 deletions packages/arb-token-bridge-ui/src/hooks/useTransactionHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,75 +250,34 @@ const useTransactionHistoryWithoutStatuses = (address: Address | undefined) => {
const { isSmartContractWallet, isLoading: isLoadingAccountType } =
useAccountType()

// Check what type of CCTP (deposit, withdrawal or all) to fetch
// We need this because of Smart Contract Wallets
const cctpTypeToFetch = useCallback(
(chainPair: ChainPair): 'deposits' | 'withdrawals' | 'all' | undefined => {
if (isLoadingAccountType || !chain) {
return undefined
}
if (isSmartContractWallet) {
// fetch based on the connected network
if (chain.id === chainPair.parentChainId) {
return 'deposits'
}
if (chain.id === chainPair.childChainId) {
return 'withdrawals'
}
return undefined
}
// EOA
return isNetwork(chainPair.parentChainId).isTestnet === isTestnetMode
? 'all'
: undefined
fionnachan marked this conversation as resolved.
Show resolved Hide resolved
},
[isSmartContractWallet, isLoadingAccountType, chain, isTestnetMode]
)

const cctpTransfersMainnet = useCctpFetching({
walletAddress: address,
l1ChainId: ChainId.Ethereum,
l2ChainId: ChainId.ArbitrumOne,
pageNumber: 0,
pageSize: cctpTypeToFetch({
parentChainId: ChainId.Ethereum,
childChainId: ChainId.ArbitrumOne
})
? 1000
: 0,
type:
cctpTypeToFetch({
parentChainId: ChainId.Ethereum,
childChainId: ChainId.ArbitrumOne
}) ?? 'all'
pageSize: 1000,
type: 'all'
})

const cctpTransfersTestnet = useCctpFetching({
walletAddress: address,
l1ChainId: ChainId.Sepolia,
l2ChainId: ChainId.ArbitrumSepolia,
pageNumber: 0,
pageSize: cctpTypeToFetch({
parentChainId: ChainId.Sepolia,
childChainId: ChainId.ArbitrumSepolia
})
? 1000
: 0,
type:
cctpTypeToFetch({
parentChainId: ChainId.Sepolia,
childChainId: ChainId.ArbitrumSepolia
}) ?? 'all'
pageSize: 1000,
type: 'all'
})

// TODO: Clean up this logic when introducing testnet/mainnet split
const combinedCctpTransfers = [
const combinedCctpMainnetTransfers = [
...(cctpTransfersMainnet.deposits?.completed || []),
...(cctpTransfersMainnet.withdrawals?.completed || []),
...(cctpTransfersMainnet.deposits?.pending || []),
...(cctpTransfersMainnet.withdrawals?.pending || [])
]

const combinedCctpTestnetTransfers = [
...(cctpTransfersTestnet.deposits?.completed || []),
...(cctpTransfersTestnet.withdrawals?.completed || []),
...(cctpTransfersMainnet.deposits?.pending || []),
...(cctpTransfersMainnet.withdrawals?.pending || []),
...(cctpTransfersTestnet.deposits?.pending || []),
...(cctpTransfersTestnet.withdrawals?.pending || [])
]
Expand Down Expand Up @@ -463,7 +422,9 @@ const useTransactionHistoryWithoutStatuses = (address: Address | undefined) => {
const transactions = [
...deposits,
...withdrawals,
...combinedCctpTransfers
...(isTestnetMode
? combinedCctpTestnetTransfers
: combinedCctpMainnetTransfers)
].flat()

return {
Expand Down
22 changes: 17 additions & 5 deletions packages/arb-token-bridge-ui/src/state/cctpState.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { BigNumber } from 'ethers'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { create } from 'zustand'
import useSWRImmutable from 'swr/immutable'
import { useInterval } from 'react-use'
import { useAccount, useChainId, useSigner } from 'wagmi'
import dayjs from 'dayjs'

import { getCctpUtils } from '@/token-bridge-sdk/cctp'
import {
Expand All @@ -14,8 +15,6 @@ import {
import { fetchCCTPDeposits, fetchCCTPWithdrawals } from '../util/cctp/fetchCCTP'
import { DepositStatus, MergedTransaction, WithdrawalStatus } from './app/state'
import { normalizeTimestamp } from './app/utils'
import { useAccount, useSigner } from 'wagmi'
import dayjs from 'dayjs'
import {
ChainDomain,
CompletedCCTPTransfer,
Expand Down Expand Up @@ -173,13 +172,18 @@ type fetchCctpParams = {
pageSize: number
enabled: boolean
}

export const useCCTPDeposits = ({
walletAddress,
l1ChainId,
pageNumber,
pageSize,
enabled
}: fetchCctpParams) => {
const { isSmartContractWallet } = useAccountType()
const chainId = useChainId()
const { isEthereumMainnetOrTestnet } = isNetwork(chainId)
Copy link
Member

Choose a reason for hiding this comment

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

i think we want to avoid using useChainId() because it returns Ethereum for chains not in wagmi config?


return useSWRImmutable(
// Only fetch when we have walletAddress
() => {
Expand All @@ -194,7 +198,9 @@ export const useCCTPDeposits = ({
walletAddress: _walletAddress,
l1ChainId: _l1ChainId,
pageNumber: _pageNumber,
pageSize: _pageSize
pageSize: _pageSize,
connectedToEthereum: isEthereumMainnetOrTestnet,
isSmartContractWallet
})
.then(deposits => parseSWRResponse(deposits, _l1ChainId))
.then(deposits => {
Expand All @@ -218,6 +224,10 @@ export const useCCTPWithdrawals = ({
pageSize,
enabled
}: fetchCctpParams) => {
const { isSmartContractWallet } = useAccountType()
const chainId = useChainId()
const { isEthereumMainnetOrTestnet } = isNetwork(chainId)

return useSWRImmutable(
// Only fetch when we have walletAddress
() => {
Expand All @@ -238,7 +248,9 @@ export const useCCTPWithdrawals = ({
walletAddress: _walletAddress,
l1ChainId: _l1ChainId,
pageNumber: _pageNumber,
pageSize: _pageSize
pageSize: _pageSize,
connectedToEthereum: isEthereumMainnetOrTestnet,
isSmartContractWallet
})
.then(withdrawals => parseSWRResponse(withdrawals, _l1ChainId))
.then(withdrawals => {
Expand Down
70 changes: 66 additions & 4 deletions packages/arb-token-bridge-ui/src/util/cctp/fetchCCTP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export type FetchParams = {
l1ChainId: ChainId
pageNumber: number
pageSize: number
connectedToEthereum: boolean
isSmartContractWallet: boolean
}

function convertStringToUsdcBigNumber(amount: string) {
Expand All @@ -26,12 +28,48 @@ function mapCCTPTransfer<T extends PendingCCTPTransfer | CompletedCCTPTransfer>(
return cctpTransfer
}

function sanitizeSmartContractWalletCctpTransfers<
T extends PendingCCTPTransfer | CompletedCCTPTransfer
>({
type,
walletAddress,
transfers,
connectedToEthereum
}: {
type: 'deposits' | 'withdrawals'
walletAddress: string
transfers: T[]
connectedToEthereum: boolean
}): T[] {
const walletAddressLowercased = walletAddress.toLowerCase()

return transfers.filter(tx => {
const { sender, recipient } = tx.messageSent
const senderLowercased = sender.toLowerCase()
const recipientLowercased = recipient.toLowerCase()

if (type === 'deposits') {
if (connectedToEthereum) {
return senderLowercased === walletAddressLowercased
}
return recipientLowercased === walletAddressLowercased
}

if (connectedToEthereum) {
return recipientLowercased === walletAddressLowercased
}
return senderLowercased === walletAddressLowercased
}) as T[]
}

async function fetchCCTP({
walletAddress,
l1ChainId,
pageNumber,
pageSize,
type
type,
connectedToEthereum,
isSmartContractWallet
}: FetchParams & { type: 'deposits' | 'withdrawals' }): Promise<
Response['data']
> {
Expand All @@ -40,7 +78,9 @@ async function fetchCCTP({
walletAddress,
l1ChainId,
pageNumber,
pageSize
pageSize,
connectedToEthereum,
isSmartContractWallet
})
)

Expand All @@ -57,9 +97,31 @@ async function fetchCCTP({
const parsedResponse: Response = await response.json()
const { pending, completed } = parsedResponse.data

const sanitizedPendingTransfers = isSmartContractWallet
? sanitizeSmartContractWalletCctpTransfers<PendingCCTPTransfer>({
type,
walletAddress,
transfers: pending,
connectedToEthereum
})
: pending

const sanitizedCompletedTransfers = isSmartContractWallet
? sanitizeSmartContractWalletCctpTransfers<CompletedCCTPTransfer>({
type,
walletAddress,
transfers: completed,
connectedToEthereum
})
: completed

return {
pending: pending.map(transfer => mapCCTPTransfer(transfer)),
completed: completed.map(transfer => mapCCTPTransfer(transfer))
pending: sanitizedPendingTransfers.map(transfer =>
mapCCTPTransfer(transfer)
),
completed: sanitizedCompletedTransfers.map(transfer =>
mapCCTPTransfer(transfer)
)
}
}

Expand Down
Loading