From e77b3967ab7143a9e22936b403e7ea742291600d Mon Sep 17 00:00:00 2001 From: Christophe Deveaux Date: Tue, 14 Nov 2023 16:42:24 +0000 Subject: [PATCH] fix: Mutate SWR cache on new CCTP transaction (#1135) Co-authored-by: Fionna Chan <13184582+fionnachan@users.noreply.github.com> Co-authored-by: spsjvc --- .../TransferPanel/TransferPanel.tsx | 44 ++++---- .../src/state/app/state.ts | 10 +- .../src/state/cctpState.ts | 102 +++++++++++------- .../src/util/cctp/fetchCCTP.ts | 5 +- 4 files changed, 100 insertions(+), 61 deletions(-) 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 9fa768edfd..c077e391e5 100644 --- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx +++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx @@ -64,10 +64,11 @@ import { isUserRejectedError } from '../../util/isUserRejectedError' import { formatAmount } from '../../util/NumberUtils' import { getUsdcTokenAddressFromSourceChainId, + useCctpFetching, useCctpState } from '../../state/cctpState' import { getAttestationHashAndMessageFromReceipt } from '../../util/cctp/getAttestationHashAndMessageFromReceipt' -import { DepositStatus } from '../../state/app/state' +import { DepositStatus, MergedTransaction } from '../../state/app/state' import { getStandardizedTimestamp } from '../../state/app/utils' import { getContracts, useCCTP } from '../../hooks/CCTP/useCCTP' import { useNativeCurrency } from '../../hooks/useNativeCurrency' @@ -176,7 +177,14 @@ export function TransferPanel() { chainId: l2Network.id }) - const { updateTransfer, setPendingTransfer } = useCctpState() + const { setPendingTransfer } = useCctpFetching({ + l1ChainId: l1Network.id, + l2ChainId: l2Network.id, + walletAddress, + pageSize: 10, + pageNumber: 0, + type: 'all' + }) const { openTransactionHistoryPanel, @@ -671,7 +679,7 @@ export function TransferPanel() { }) } - setPendingTransfer({ + const newTransfer: MergedTransaction = { txId: depositForBurnTx.hash, asset: 'USDC', assetType: AssetType.ERC20, @@ -689,13 +697,10 @@ export function TransferPanel() { isCctp: true, tokenAddress: getUsdcTokenAddressFromSourceChainId(sourceChainId), cctpData: { - sourceChainId, - attestationHash: null, - messageBytes: null, - receiveMessageTransactionHash: null, - receiveMessageTimestamp: null + sourceChainId } - }) + } + setPendingTransfer(newTransfer, isDeposit ? 'deposit' : 'withdrawal') if (isDeposit) { showCctpDepositsTransactions() @@ -717,15 +722,18 @@ export function TransferPanel() { } if (messageBytes && attestationHash) { - updateTransfer({ - txId: depositForBurnTx.hash, - blockNum: depositTxReceipt.blockNumber, - status: 'Unconfirmed', - cctpData: { - attestationHash, - messageBytes - } - }) + setPendingTransfer( + { + txId: depositForBurnTx.hash, + blockNum: depositTxReceipt.blockNumber, + status: 'Unconfirmed', + cctpData: { + attestationHash, + messageBytes + } + }, + isDeposit ? 'deposit' : 'withdrawal' + ) } } catch (e) { } finally { diff --git a/packages/arb-token-bridge-ui/src/state/app/state.ts b/packages/arb-token-bridge-ui/src/state/app/state.ts index 7f452596ef..738543d022 100644 --- a/packages/arb-token-bridge-ui/src/state/app/state.ts +++ b/packages/arb-token-bridge-ui/src/state/app/state.ts @@ -73,11 +73,11 @@ export interface MergedTransaction { chainId?: number parentChainId?: number cctpData?: { - sourceChainId: CCTPSupportedChainId - attestationHash: `0x${string}` | null - messageBytes: string | null - receiveMessageTransactionHash: `0x${string}` | null - receiveMessageTimestamp: string | null + sourceChainId?: CCTPSupportedChainId + attestationHash?: `0x${string}` | null + messageBytes?: string | null + receiveMessageTransactionHash?: `0x${string}` | null + receiveMessageTimestamp?: string | null } } diff --git a/packages/arb-token-bridge-ui/src/state/cctpState.ts b/packages/arb-token-bridge-ui/src/state/cctpState.ts index 4b10d2a644..1166f9ad75 100644 --- a/packages/arb-token-bridge-ui/src/state/cctpState.ts +++ b/packages/arb-token-bridge-ui/src/state/cctpState.ts @@ -127,13 +127,14 @@ function parseTransferToMergedTransaction( } } +type ParsedResponse = { + pending: MergedTransaction[] + completed: MergedTransaction[] +} function parseSWRResponse( { pending, completed }: Response['data'], chainId: ChainId -): { - pending: MergedTransaction[] - completed: MergedTransaction[] -} { +): ParsedResponse { return { pending: pending.map(pendingDeposit => parseTransferToMergedTransaction(pendingDeposit, chainId) @@ -158,20 +159,14 @@ export const useCCTPDeposits = ({ pageSize, enabled }: fetchCctpParams) => { - const { data, error, isLoading } = useSWRImmutable( + return useSWRImmutable( // Only fetch when we have walletAddress () => { if (!walletAddress || !enabled) { return null } - return [ - walletAddress, - l1ChainId, - pageNumber, - pageSize, - 'cctp-deposits' - ] as const + return [walletAddress, l1ChainId, pageNumber, pageSize, 'cctp-deposits'] }, ([_walletAddress, _l1ChainId, _pageNumber, _pageSize]) => fetchCCTPDeposits({ @@ -181,8 +176,6 @@ export const useCCTPDeposits = ({ pageSize: _pageSize }).then(deposits => parseSWRResponse(deposits, _l1ChainId)) ) - - return { data, error, isLoading } } export const useCCTPWithdrawals = ({ @@ -192,7 +185,7 @@ export const useCCTPWithdrawals = ({ pageSize, enabled }: fetchCctpParams) => { - const { data, error, isLoading, isValidating } = useSWRImmutable( + return useSWRImmutable( // Only fetch when we have walletAddress () => { if (!walletAddress || !enabled) { @@ -205,7 +198,7 @@ export const useCCTPWithdrawals = ({ pageNumber, pageSize, 'cctp-withdrawals' - ] as const + ] }, ([_walletAddress, _l1ChainId, _pageNumber, _pageSize]) => fetchCCTPWithdrawals({ @@ -215,8 +208,6 @@ export const useCCTPWithdrawals = ({ pageSize: _pageSize }).then(withdrawals => parseSWRResponse(withdrawals, _l1ChainId)) ) - - return { data, error, isLoading, isValidating } } type PartialMergedTransaction = Partial> & { @@ -231,7 +222,6 @@ type CctpStore = { completed: MergedTransaction[] }) => void resetTransfers: () => void - setPendingTransfer: (transfer: MergedTransaction) => void updateTransfer: (transfer: PartialMergedTransaction) => void } @@ -270,16 +260,6 @@ const useCctpStore = create((set, get) => ({ transfersIds: [...ids] }) }, - setPendingTransfer: async transfer => { - return set(prevState => ({ - transfers: { - ...prevState.transfers, - [transfer.txId]: transfer - }, - // Set the new transfer as first item (showing first in pending transaction) - transfersIds: [...new Set([transfer.txId].concat(prevState.transfersIds))] - })) - }, updateTransfer: transfer => { const prevTransfer = get().transfers[transfer.txId] if (!prevTransfer) { @@ -308,7 +288,6 @@ export function useCctpState() { transfers, resetTransfers, setTransfers, - setPendingTransfer, updateTransfer } = useCctpStore() @@ -344,7 +323,6 @@ export function useCctpState() { }, [transfersIds, transfers]) return { - setPendingTransfer, resetTransfers, setTransfers, transfersIds, @@ -367,6 +345,7 @@ export function useUpdateCctpTransactions() { async (tx: MergedTransaction) => { const provider = tx.direction === 'deposit' ? l1Provider : l2Provider const receipt = await provider.getTransactionReceipt(tx.txId) + return { receipt, tx @@ -466,7 +445,8 @@ export function useCctpFetching({ const { data: deposits, isLoading: isLoadingDeposits, - error: depositsError + error: depositsError, + mutate: mutateDeposits } = useCCTPDeposits({ l1ChainId, walletAddress, @@ -478,7 +458,8 @@ export function useCctpFetching({ const { data: withdrawals, isLoading: isLoadingWithdrawals, - error: withdrawalsError + error: withdrawalsError, + mutate: mutateWithdrawals } = useCCTPWithdrawals({ l1ChainId, walletAddress, @@ -500,13 +481,62 @@ export function useCctpFetching({ } }, [withdrawals, setTransfers]) + const setPendingTransfer = useCallback( + (transfer: PartialMergedTransaction, type: 'deposit' | 'withdrawal') => { + const mutate = type === 'deposit' ? mutateDeposits : mutateWithdrawals + mutate( + { + pending: [transfer as MergedTransaction], + completed: [] + }, + { + populateCache(result: ParsedResponse, currentData) { + const transfer = result.pending[0] + if (!currentData || !transfer) { + return result + } + const index = currentData.pending.findIndex( + tx => tx.txId === transfer.txId + ) + const existingTransfer = currentData.pending[index] + if (existingTransfer) { + const { cctpData, ...txData } = existingTransfer + const { cctpData: resultCctpData, ...resultTxData } = transfer + + currentData.pending[index] = { + ...txData, + ...resultTxData, + cctpData: { + ...cctpData, + ...resultCctpData + } + } + + return currentData + } + + return { + pending: [...result.pending, ...currentData.pending], + completed: [...result.completed, ...currentData.completed] + } + }, + revalidate: false + } + ) + }, + [mutateDeposits, mutateWithdrawals] + ) + return { deposits, withdrawals, isLoadingDeposits, isLoadingWithdrawals, depositsError, - withdrawalsError + withdrawalsError, + mutateDeposits, + mutateWithdrawals, + setPendingTransfer } } @@ -590,7 +620,7 @@ export function useClaimCctp(tx: MergedTransaction) { } export function getL1ChainIdFromSourceChain(tx: MergedTransaction) { - if (!tx.cctpData) { + if (!tx.cctpData?.sourceChainId) { return ChainId.Mainnet } @@ -603,7 +633,7 @@ export function getL1ChainIdFromSourceChain(tx: MergedTransaction) { } export function getTargetChainIdFromSourceChain(tx: MergedTransaction) { - if (!tx.cctpData) { + if (!tx.cctpData?.sourceChainId) { return ChainId.Mainnet } diff --git a/packages/arb-token-bridge-ui/src/util/cctp/fetchCCTP.ts b/packages/arb-token-bridge-ui/src/util/cctp/fetchCCTP.ts index 32cc43bebd..dd6db6a45a 100644 --- a/packages/arb-token-bridge-ui/src/util/cctp/fetchCCTP.ts +++ b/packages/arb-token-bridge-ui/src/util/cctp/fetchCCTP.ts @@ -61,14 +61,15 @@ async function fetchCCTP({ } } +type FetchCctpResponse = Response['data'] export async function fetchCCTPDeposits( params: FetchParams -): Promise { +): Promise { return fetchCCTP({ ...params, type: 'deposits' }) } export async function fetchCCTPWithdrawals( params: FetchParams -): Promise { +): Promise { return fetchCCTP({ ...params, type: 'withdrawals' }) }