diff --git a/packages/arb-token-bridge-ui/src/hooks/arbTokenBridge.types.ts b/packages/arb-token-bridge-ui/src/hooks/arbTokenBridge.types.ts index c4b4451e12..6885fffdc7 100644 --- a/packages/arb-token-bridge-ui/src/hooks/arbTokenBridge.types.ts +++ b/packages/arb-token-bridge-ui/src/hooks/arbTokenBridge.types.ts @@ -141,7 +141,7 @@ export interface ArbTokenBridgeEth { triggerOutbox: (params: { event: L2ToL1EventResultPlus l1Signer: Signer - }) => Promise + }) => Promise } export interface ArbTokenBridgeToken { @@ -153,7 +153,7 @@ export interface ArbTokenBridgeToken { triggerOutbox: (params: { event: L2ToL1EventResultPlus l1Signer: Signer - }) => Promise + }) => Promise } export interface TransactionActions { diff --git a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts index 955e15a097..a31169b079 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useArbTokenBridge.ts @@ -3,14 +3,8 @@ import { Chain, useAccount } from 'wagmi' import { BigNumber } from 'ethers' import { Signer } from '@ethersproject/abstract-signer' import { JsonRpcProvider } from '@ethersproject/providers' -import { useLocalStorage } from '@rehooks/local-storage' import { TokenList } from '@uniswap/token-lists' -import { - EventArgs, - ChildToParentMessage, - ChildToParentTransactionEvent -} from '@arbitrum/sdk' -import { L2ToL1TransactionEvent as ClassicL2ToL1TransactionEvent } from '@arbitrum/sdk/dist/lib/abi/ArbSys' +import { ChildToParentMessage } from '@arbitrum/sdk' import useTransactions from './useTransactions' import { @@ -41,24 +35,6 @@ export const wait = (ms = 0) => { return new Promise(res => setTimeout(res, ms)) } -function isClassicL2ToL1TransactionEvent( - event: ChildToParentTransactionEvent -): event is EventArgs { - return typeof (event as any).batchNumber !== 'undefined' -} - -export function getExecutedMessagesCacheKey({ - event, - l2ChainId -}: { - event: L2ToL1EventResult - l2ChainId: number -}) { - return isClassicL2ToL1TransactionEvent(event) - ? `l2ChainId: ${l2ChainId}, batchNumber: ${event.batchNumber.toString()}, indexInBatch: ${event.indexInBatch.toString()}` - : `l2ChainId: ${l2ChainId}, position: ${event.position.toString()}` -} - export function getUniqueIdOrHashFromEvent( event: L2ToL1EventResult ): BigNumber { @@ -121,20 +97,6 @@ export const useArbTokenBridge = ( walletAddress: destinationAddress }) - interface ExecutedMessagesCache { - [id: string]: boolean - } - - const [executedMessagesCache, setExecutedMessagesCache] = - useLocalStorage( - 'arbitrum:bridge:executed-messages', - {} - ) as [ - ExecutedMessagesCache, - React.Dispatch, - React.Dispatch - ] - const l1NetworkID = useMemo(() => String(l1.network.id), [l1.network.id]) const [transactions, { addTransaction, updateTransaction }] = @@ -431,7 +393,7 @@ export const useArbTokenBridge = ( } if (!walletAddress) { - return + throw new Error('Wallet address not found') } const parentChainProvider = getProviderForChainId(event.parentChainId) @@ -443,13 +405,8 @@ export const useArbTokenBridge = ( parentChainProvider ) const res = await messageWriter.execute(childChainProvider) - const rec = await res.wait() - if (rec.status === 1) { - addToExecutedMessagesCache([event]) - } - return rec } @@ -487,7 +444,7 @@ export const useArbTokenBridge = ( } if (!walletAddress) { - return + throw new Error('Wallet address not found') } const parentChainProvider = getProviderForChainId(event.parentChainId) @@ -500,31 +457,11 @@ export const useArbTokenBridge = ( ) const res = await messageWriter.execute(childChainProvider) - const rec = await res.wait() - if (rec.status === 1) { - addToExecutedMessagesCache([event]) - } - return rec } - function addToExecutedMessagesCache(events: L2ToL1EventResult[]) { - const added: { [cacheKey: string]: boolean } = {} - - events.forEach((event: L2ToL1EventResult) => { - const cacheKey = getExecutedMessagesCacheKey({ - event, - l2ChainId: l2.network.id - }) - - added[cacheKey] = true - }) - - setExecutedMessagesCache({ ...executedMessagesCache, ...added }) - } - return { bridgeTokens, eth: { diff --git a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts index f174dc8fb3..df2ee75743 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useClaimWithdrawal.ts @@ -15,6 +15,7 @@ import { fetchErc20Data } from '../util/TokenUtils' import { fetchNativeCurrency } from './useNativeCurrency' import { getProviderForChainId } from '@/token-bridge-sdk/utils' import { captureSentryErrorWithExtraData } from '../util/SentryUtils' +import { useExecutedMessagesCache } from './useExecutedMessagesCache' export type UseClaimWithdrawalResult = { claim: () => Promise @@ -31,6 +32,7 @@ export function useClaimWithdrawal( const { data: signer } = useSigner({ chainId: tx.parentChainId }) const { updatePendingTransaction } = useTransactionHistory(address) const [isClaiming, setIsClaiming] = useState(false) + const [, addToExecutedMessagesCache] = useExecutedMessagesCache() const claim = useCallback(async () => { if (isClaiming || !tx.isWithdrawal || tx.isCctp) { @@ -95,6 +97,10 @@ export function useClaimWithdrawal( l1Signer: signer }) } + + if (res.status === 1) { + addToExecutedMessagesCache([event]) + } } catch (error: any) { err = error } finally { @@ -132,7 +138,8 @@ export function useClaimWithdrawal( isClaiming, signer, tx, - updatePendingTransaction + updatePendingTransaction, + addToExecutedMessagesCache ]) return { claim, isClaiming } diff --git a/packages/arb-token-bridge-ui/src/hooks/useExecutedMessagesCache.ts b/packages/arb-token-bridge-ui/src/hooks/useExecutedMessagesCache.ts new file mode 100644 index 0000000000..f07c058a50 --- /dev/null +++ b/packages/arb-token-bridge-ui/src/hooks/useExecutedMessagesCache.ts @@ -0,0 +1,70 @@ +import { useCallback } from 'react' +import { useLocalStorage } from '@rehooks/local-storage' +import { EventArgs, ChildToParentTransactionEvent } from '@arbitrum/sdk' +import { L2ToL1TransactionEvent as ClassicL2ToL1TransactionEvent } from '@arbitrum/sdk/dist/lib/abi/ArbSys' + +import { L2ToL1EventResult } from './arbTokenBridge.types' +import { useNetworksRelationship } from './useNetworksRelationship' +import { useNetworks } from './useNetworks' + +const LOCAL_STORAGE_KEY = 'arbitrum:bridge:executed-messages' + +export type ExecutedMessagesCache = { + [id: string]: boolean +} + +export type ExecutedMessagesCacheKeyParams = { + event: L2ToL1EventResult + childChainId: number +} + +function isClassicL2ToL1TransactionEvent( + event: ChildToParentTransactionEvent +): event is EventArgs { + return typeof (event as any).batchNumber !== 'undefined' +} + +function getCacheKey({ event, childChainId }: ExecutedMessagesCacheKeyParams) { + return isClassicL2ToL1TransactionEvent(event) + ? `childChainId: ${childChainId}, batchNumber: ${event.batchNumber.toString()}, indexInBatch: ${event.indexInBatch.toString()}` + : `childChainId: ${childChainId}, position: ${event.position.toString()}` +} + +export function checkExecutedMessagesCache( + params: ExecutedMessagesCacheKeyParams +): boolean { + const cache = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY) || '{}') + return typeof cache[getCacheKey(params)] !== 'undefined' +} + +export function useExecutedMessagesCache() { + const [networks] = useNetworks() + const { childChain } = useNetworksRelationship(networks) + + const [executedMessagesCache, setExecutedMessagesCache] = + useLocalStorage(LOCAL_STORAGE_KEY, {}) as [ + ExecutedMessagesCache, + React.Dispatch, + React.Dispatch + ] + + const add = useCallback( + (events: L2ToL1EventResult[]) => { + const added: { [cacheKey: string]: boolean } = {} + + events.forEach((event: L2ToL1EventResult) => { + const cacheKey = getCacheKey({ + event, + childChainId: childChain.id + }) + + added[cacheKey] = true + }) + + setExecutedMessagesCache({ ...executedMessagesCache, ...added }) + }, + [executedMessagesCache, setExecutedMessagesCache, childChain.id] + ) + + return [executedMessagesCache, add] as const +} diff --git a/packages/arb-token-bridge-ui/src/util/withdrawals/helpers.ts b/packages/arb-token-bridge-ui/src/util/withdrawals/helpers.ts index 82e7706695..80282cee7e 100644 --- a/packages/arb-token-bridge-ui/src/util/withdrawals/helpers.ts +++ b/packages/arb-token-bridge-ui/src/util/withdrawals/helpers.ts @@ -16,7 +16,7 @@ import { OutgoingMessageState, WithdrawalInitiated } from '../../hooks/arbTokenBridge.types' -import { getExecutedMessagesCacheKey } from '../../hooks/useArbTokenBridge' +import { checkExecutedMessagesCache } from '../../hooks/useExecutedMessagesCache' import { fetchNativeCurrency } from '../../hooks/useNativeCurrency' /** @@ -103,15 +103,7 @@ export async function getOutgoingMessageState( l2Provider: Provider, l2ChainID: number ) { - const cacheKey = getExecutedMessagesCacheKey({ - event, - l2ChainId: l2ChainID - }) - - const executedMessagesCache = JSON.parse( - localStorage.getItem('arbitrum:bridge:executed-messages') || '{}' - ) - if (executedMessagesCache[cacheKey]) { + if (checkExecutedMessagesCache({ event, childChainId: l2ChainID })) { return OutgoingMessageState.EXECUTED }