Repository: hemilabs/ui-monorepo
Pull request #1109: Add step to wait for Hemi deposit availability
Author: login: ArturDolzan
Labels: name: enhancement
description: New feature or request
name: Tunnel
description: Related to the Tunnel app
Created: 2025-04-01T15:32:41Z | Merged: 2025-04-02T15:19:53Z

### Description

This PR introduces a new step in the deposit flow that informs the user to wait approximately three minutes for their funds to become available in Hemi.

To achieve this, we added a call to `crossChainMessenger.getMessageStatus` to check when the transaction has been fully relayed and acknowledged.

Additionally, an `invalidateQueries` call was added to `evmDepositsStatusUpdater.tsx` to ensure the balance is refreshed immediately after the deposit is confirmed by getMessageStatus.

With these changes, the user’s balance is now updated almost in real time.

### Screenshots


https://github.com/user-attachments/assets/35465e88-7b64-4769-ad3e-308a3d56a6db


### Related issue(s)

Related to #1013

### Checklist

- [x] Manual testing passed.
- [ ] Automated tests added, or N/A.
- [ ] Documentation updated, or N/A.
- [ ] Environment variables set in CI, or N/A.



Diff:
diff --git a/webapp/app/[locale]/tunnel/_components/reviewOperation/reviewEvmDeposit.tsx b/webapp/app/[locale]/tunnel/_components/reviewOperation/reviewEvmDeposit.tsx
index 6d39bcdc4..dbf78cc4f 100644
--- a/webapp/app/[locale]/tunnel/_components/reviewOperation/reviewEvmDeposit.tsx
+++ b/webapp/app/[locale]/tunnel/_components/reviewOperation/reviewEvmDeposit.tsx
@@ -7,7 +7,11 @@ import { useToken } from 'hooks/useToken'
 import { useTranslations } from 'next-intl'
 import Skeleton from 'react-loading-skeleton'
 import { EvmToken } from 'types/token'
-import { EvmDepositOperation, EvmDepositStatus } from 'types/tunnel'
+import {
+  EvmDepositOperation,
+  EvmDepositStatus,
+  ExpectedWaitTimeMinutesGetFundsHemi,
+} from 'types/tunnel'
 import { getNativeToken, isNativeToken } from 'utils/nativeToken'
 import { formatUnits } from 'viem'
 
@@ -45,6 +49,7 @@ const ReviewContent = function ({
     toToken,
   })
   const t = useTranslations('tunnel-page.review-deposit')
+  const tCommon = useTranslations('common')
 
   const steps: StepPropsWithoutPosition[] = []
 
@@ -78,10 +83,17 @@ const ReviewContent = function ({
       [EvmDepositStatus.DEPOSIT_TX_CONFIRMED]: ProgressStatus.COMPLETED,
       [EvmDepositStatus.DEPOSIT_TX_FAILED]: ProgressStatus.FAILED,
       [EvmDepositStatus.DEPOSIT_TX_PENDING]: ProgressStatus.PROGRESS,
+      [EvmDepositStatus.DEPOSIT_RELAYED]: ProgressStatus.COMPLETED,
+    }
+
+    const postStatusMap = {
+      [EvmDepositStatus.DEPOSIT_TX_CONFIRMED]: ProgressStatus.PROGRESS,
+      [EvmDepositStatus.DEPOSIT_RELAYED]: ProgressStatus.COMPLETED,
     }
     return {
       description:
-        depositStatus === EvmDepositStatus.DEPOSIT_TX_CONFIRMED
+        depositStatus === EvmDepositStatus.DEPOSIT_TX_CONFIRMED ||
+        EvmDepositStatus.DEPOSIT_RELAYED
           ? t('deposit-completed')
           : t('initiate-deposit'),
       explorerChainId: deposit.l1ChainId,
@@ -98,11 +110,25 @@ const ReviewContent = function ({
             token: getNativeToken(fromChain.id),
           }
         : undefined,
+      postAction: {
+        description: tCommon('wait-minutes', {
+          minutes: ExpectedWaitTimeMinutesGetFundsHemi,
+        }),
+        status: postStatusMap[depositStatus],
+      },
       status: statusMap[depositStatus],
       txHash: deposit.transactionHash,
     }
   }
 
+  const getFundsHemiStep = (): StepPropsWithoutPosition => ({
+    description: t('get-your-funds-on-hemi'),
+    status:
+      depositStatus === EvmDepositStatus.DEPOSIT_RELAYED
+        ? ProgressStatus.COMPLETED
+        : ProgressStatus.NOT_READY,
+  })
+
   // Show the approval only if it's a not native token and there is a approval.
   // Note that for past re-sync transactions, the approvalHash won't be available,
   // as we can't see if a user has approved a token before the actual deposit (they are different transactions
@@ -112,6 +138,7 @@ const ReviewContent = function ({
   }
 
   steps.push(getDepositStep())
+  steps.push(getFundsHemiStep())
 
   return (
     <Operation
diff --git a/webapp/app/[locale]/tunnel/transaction-history/_components/depositStatus.tsx b/webapp/app/[locale]/tunnel/transaction-history/_components/depositStatus.tsx
index 35c51c42a..7861cb65d 100644
--- a/webapp/app/[locale]/tunnel/transaction-history/_components/depositStatus.tsx
+++ b/webapp/app/[locale]/tunnel/transaction-history/_components/depositStatus.tsx
@@ -1,5 +1,10 @@
 import { useTranslations } from 'next-intl'
-import { BtcDepositStatus, DepositTunnelOperation } from 'types/tunnel'
+import {
+  BtcDepositStatus,
+  DepositTunnelOperation,
+  EvmDepositStatus,
+  ExpectedWaitTimeMinutesGetFundsHemi,
+} from 'types/tunnel'
 import { isBtcDeposit } from 'utils/tunnel'
 
 import { TxStatus } from './txStatus'
@@ -12,9 +17,21 @@ export const DepositStatus = function ({ deposit }: Props) {
   const t = useTranslations()
 
   if (!isBtcDeposit(deposit)) {
-    // Evm deposits are always successful if listed
-    return <TxStatus.Success />
+    const evmStatuses = {
+      [EvmDepositStatus.DEPOSIT_TX_CONFIRMED]: (
+        <TxStatus.InStatus
+          text={t('common.wait-minutes', {
+            minutes: ExpectedWaitTimeMinutesGetFundsHemi,
+          })}
+        />
+      ),
+      // Check if funds have been minted in Hemi
+      [EvmDepositStatus.DEPOSIT_RELAYED]: <TxStatus.Success />,
+    }
+
+    return evmStatuses[deposit.status] ?? '-'
   }
+
   const statuses = {
     [BtcDepositStatus.BTC_TX_PENDING]: (
       <TxStatus.InStatus text={t('transaction-history.waiting-confirmation')} />
diff --git a/webapp/components/reviewOperation/step.tsx b/webapp/components/reviewOperation/step.tsx
index 5476a3599..88f5d1b6e 100644
--- a/webapp/components/reviewOperation/step.tsx
+++ b/webapp/components/reviewOperation/step.tsx
@@ -65,7 +65,9 @@ const Completed = function ({
             <>
               <GreenCheckIcon />
               <span className="mr-auto text-emerald-500">{t('confirmed')}</span>
-              <SeeOnExplorer chainId={explorerChainId} txHash={txHash} />
+              {explorerChainId && txHash && (
+                <SeeOnExplorer chainId={explorerChainId} txHash={txHash} />
+              )}
             </>
           ) : null
         }
@@ -126,7 +128,9 @@ const Progress = function ({
           <>
             <ClockIcon />
             <span className="mr-auto text-neutral-500">{t('pending')}</span>
-            <SeeOnExplorer chainId={explorerChainId} txHash={txHash} />
+            {explorerChainId && txHash && (
+              <SeeOnExplorer chainId={explorerChainId} txHash={txHash} />
+            )}
           </>
         }
         top={{
@@ -191,7 +195,9 @@ const Failed = function ({
             <>
               <RedErrorIcon />
               <span className="mr-auto text-rose-500">{t('error')}</span>
-              <SeeOnExplorer chainId={explorerChainId} txHash={txHash} />
+              {explorerChainId && txHash && (
+                <SeeOnExplorer chainId={explorerChainId} txHash={txHash} />
+              )}
             </>
           ) : null
         }
diff --git a/webapp/context/tunnelHistoryContext/evmDepositsStatusUpdater.tsx b/webapp/context/tunnelHistoryContext/evmDepositsStatusUpdater.tsx
index a78c32af3..152e07d1e 100644
--- a/webapp/context/tunnelHistoryContext/evmDepositsStatusUpdater.tsx
+++ b/webapp/context/tunnelHistoryContext/evmDepositsStatusUpdater.tsx
@@ -1,9 +1,12 @@
+import { useQueryClient } from '@tanstack/react-query'
 import { WithWorker } from 'components/withWorker'
+import { useNativeTokenBalance, useTokenBalance } from 'hooks/useBalance'
 import { useConnectedToUnsupportedEvmChain } from 'hooks/useConnectedToUnsupportedChain'
 import { useEvmDeposits } from 'hooks/useEvmDeposits'
 import { useTunnelHistory } from 'hooks/useTunnelHistory'
 import { useEffect } from 'react'
-import { EvmDepositOperation } from 'types/tunnel'
+import { EvmDepositOperation, EvmDepositStatus } from 'types/tunnel'
+import { isNativeAddress } from 'utils/nativeToken'
 import { isPendingOperation } from 'utils/tunnel'
 import { hasKeys } from 'utils/utilities'
 import { useAccount } from 'wagmi'
@@ -17,6 +20,14 @@ const WatchEvmDeposit = function ({
   worker: AppToWorker
 }) {
   const { updateDeposit } = useTunnelHistory()
+  const { queryKey: erc20BalanceQueryKey } = useTokenBalance(
+    deposit.l2ChainId,
+    deposit.l2Token,
+  )
+  const { queryKey: nativeTokenBalanceQueryKey } = useNativeTokenBalance(
+    deposit.l2ChainId,
+  )
+  const queryClient = useQueryClient()
 
   useEffect(
     function watchDepositUpdates() {
@@ -39,6 +50,15 @@ const WatchEvmDeposit = function ({
         // next interval will poll again
         hasWorkedPostedBack = true
         if (hasKeys(updates)) {
+          // This is needed to invalidate the balance query when the deposit is done
+          // so the balance is updated in the UI instantly
+          if (updates.status === EvmDepositStatus.DEPOSIT_RELAYED) {
+            const balanceQueryKey = isNativeAddress(deposit.l1Token)
+              ? nativeTokenBalanceQueryKey
+              : erc20BalanceQueryKey
+
+            queryClient.invalidateQueries({ queryKey: balanceQueryKey })
+          }
           updateDeposit(deposit, updates)
         }
       }
@@ -70,7 +90,14 @@ const WatchEvmDeposit = function ({
         clearInterval(intervalId)
       }
     },
-    [deposit, updateDeposit, worker],
+    [
+      erc20BalanceQueryKey,
+      deposit,
+      nativeTokenBalanceQueryKey,
+      queryClient,
+      updateDeposit,
+      worker,
+    ],
   )
 
   return null
diff --git a/webapp/messages/en.json b/webapp/messages/en.json
index f0e930f72..8de1dc4e0 100644
--- a/webapp/messages/en.json
+++ b/webapp/messages/en.json
@@ -302,6 +302,7 @@
       "confirm-deposit": "Confirm deposit",
       "deposit-finalized": "Deposit finalized",
       "deposit-on-its-way": "Your deposit is on its way! Please wait while the blockchain finalizes the transaction.",
+      "get-your-funds-on-hemi": "Get your funds on Hemi",
       "initiate-deposit": "Initiate deposit",
       "review-deposit": "Review Deposit",
       "we-could-not-process-this-deposit": "We couldn't process this deposit automatically",
diff --git a/webapp/messages/es.json b/webapp/messages/es.json
index 57532c59b..fba365013 100644
--- a/webapp/messages/es.json
+++ b/webapp/messages/es.json
@@ -302,6 +302,7 @@
       "confirm-deposit": "Confirmar depósito",
       "deposit-finalized": "Depósito finalizado",
       "deposit-on-its-way": "¡Su depósito está en camino! Por favor espere mientras la blockchain confirma la transacción.",
+      "get-your-funds-on-hemi": "Obtenga sus fondos en Hemi",
       "initiate-deposit": "Iniciar depósito",
       "review-deposit": "Revisar Depósito",
       "we-could-not-process-this-deposit": "No pudimos procesar su depósito automáticamente",
diff --git a/webapp/test/utils/tunnel.test.ts b/webapp/test/utils/tunnel.test.ts
index 2c6224aeb..9b873f06a 100644
--- a/webapp/test/utils/tunnel.test.ts
+++ b/webapp/test/utils/tunnel.test.ts
@@ -82,11 +82,11 @@ describe('utils/tunnel', function () {
           expect(isPendingOperation(operation)).toBe(true)
         })
 
-        it('should return false if status is DEPOSIT_TX_CONFIRMED', function () {
+        it('should return false if status is DEPOSIT_RELAYED', function () {
           const operation = {
             direction: MessageDirection.L1_TO_L2,
             l1ChainId: 123456,
-            status: EvmDepositStatus.DEPOSIT_TX_CONFIRMED,
+            status: EvmDepositStatus.DEPOSIT_RELAYED,
           }
           // @ts-expect-error Ignore operation fields not required for this test
           expect(isPendingOperation(operation)).toBe(false)
diff --git a/webapp/test/utils/watch/evmDeposits.test.ts b/webapp/test/utils/watch/evmDeposits.test.ts
index af1cfc80e..64afcc0de 100644
--- a/webapp/test/utils/watch/evmDeposits.test.ts
+++ b/webapp/test/utils/watch/evmDeposits.test.ts
@@ -1,7 +1,7 @@
 import { EvmDepositStatus, type EvmDepositOperation } from 'types/tunnel'
 import { getEvmBlock, getEvmTransactionReceipt } from 'utils/evmApi'
 import { watchEvmDeposit } from 'utils/watch/evmDeposits'
-import { sepolia } from 'viem/chains'
+import { hemiSepolia, sepolia } from 'viem/chains'
 import { describe, it, expect, vi } from 'vitest'
 
 vi.mock('utils/evmApi', () => ({
@@ -9,9 +9,24 @@ vi.mock('utils/evmApi', () => ({
   getEvmTransactionReceipt: vi.fn(),
 }))
 
+vi.mock('eth-rpc-cache', () => ({
+  createEthRpcCache: vi.fn(() => ({
+    request: vi.fn(),
+  })),
+  perBlockStrategy: vi.fn(),
+  permanentStrategy: vi.fn(),
+}))
+
+vi.mock('utils/crossChainMessenger', () => ({
+  createQueuedCrossChainMessenger: vi.fn(() => ({
+    getMessageStatus: vi.fn(() => 0),
+  })),
+}))
+
 // @ts-expect-error only use the minimum required properties
 const deposit: EvmDepositOperation = {
   l1ChainId: sepolia.id,
+  l2ChainId: hemiSepolia.id,
   status: EvmDepositStatus.DEPOSIT_TX_PENDING,
   transactionHash: '0x0000000000000000000000000000000000000005',
 }
@@ -22,19 +37,14 @@ const block = { timestamp: BigInt(1630000000) }
 describe('watchEvmDeposit', function () {
   it('should not return any update if the transaction is still pending', async function () {
     vi.mocked(getEvmTransactionReceipt).mockResolvedValue(null)
-
     const updates = await watchEvmDeposit(deposit)
-
     expect(updates).toEqual({})
     expect(getEvmBlock).not.toHaveBeenCalled()
   })
-
   it(`should return the new status set to ${EvmDepositStatus.DEPOSIT_TX_CONFIRMED} if the transaction was confirmed`, async function () {
     vi.mocked(getEvmTransactionReceipt).mockResolvedValue(receipt)
     vi.mocked(getEvmBlock).mockResolvedValue(block)
-
     const updates = await watchEvmDeposit(deposit)
-
     expect(updates.status).toBe(EvmDepositStatus.DEPOSIT_TX_CONFIRMED)
     expect(updates.blockNumber).toBe(Number(receipt.blockNumber))
     expect(updates.timestamp).toBe(Number(block.timestamp))
@@ -47,16 +57,13 @@ describe('watchEvmDeposit', function () {
       deposit.l1ChainId,
     )
   })
-
   it(`should update status to ${EvmDepositStatus.DEPOSIT_TX_FAILED} if the transaction reverted`, async function () {
     vi.mocked(getEvmTransactionReceipt).mockResolvedValue({
       ...receipt,
       status: 'reverted',
     })
     vi.mocked(getEvmBlock).mockResolvedValue(block)
-
     const updates = await watchEvmDeposit(deposit)
-
     expect(updates.status).toBe(EvmDepositStatus.DEPOSIT_TX_FAILED)
     expect(updates.blockNumber).toBe(Number(receipt.blockNumber))
     expect(updates.timestamp).toBe(Number(block.timestamp))
diff --git a/webapp/types/tunnel.ts b/webapp/types/tunnel.ts
index 251c20487..40627672d 100644
--- a/webapp/types/tunnel.ts
+++ b/webapp/types/tunnel.ts
@@ -111,8 +111,12 @@ export const enum EvmDepositStatus {
   DEPOSIT_TX_FAILED = 4,
   // Approval failed
   APPROVAL_TX_FAILED = 5,
+  // Funds have been minted in Hemi
+  DEPOSIT_RELAYED = 6,
 }
 
+export const ExpectedWaitTimeMinutesGetFundsHemi = 3
+
 type CommonOperation = {
   amount: string
   blockNumber?: number
diff --git a/webapp/utils/tunnel.ts b/webapp/utils/tunnel.ts
index 9bd072386..66c37393d 100644
--- a/webapp/utils/tunnel.ts
+++ b/webapp/utils/tunnel.ts
@@ -105,7 +105,7 @@ const btcWithdrawCompletedActions = [
 ]
 
 const evmDepositCompletedActions = [
-  EvmDepositStatus.DEPOSIT_TX_CONFIRMED,
+  EvmDepositStatus.DEPOSIT_RELAYED,
   EvmDepositStatus.DEPOSIT_TX_FAILED,
 ]
 
diff --git a/webapp/utils/watch/evmDeposits.ts b/webapp/utils/watch/evmDeposits.ts
index 2098b6902..f7abbe431 100644
--- a/webapp/utils/watch/evmDeposits.ts
+++ b/webapp/utils/watch/evmDeposits.ts
@@ -1,9 +1,36 @@
+import { MessageStatus } from '@eth-optimism/sdk'
+import pMemoize from 'promise-mem'
 import { EvmDepositStatus, type EvmDepositOperation } from 'types/tunnel'
+import { findChainById } from 'utils/chain'
+import { createQueuedCrossChainMessenger } from 'utils/crossChainMessenger'
 import { getEvmBlock, getEvmTransactionReceipt } from 'utils/evmApi'
+import { createProvider } from 'utils/providers'
+import { Chain } from 'viem'
+
+const getCrossChainMessenger = pMemoize(
+  function (l1Chain: Chain, l2Chain: Chain) {
+    const l1Provider = createProvider(l1Chain)
+
+    const l2Provider = createProvider(l2Chain)
+
+    return createQueuedCrossChainMessenger({
+      l1ChainId: l1Chain.id,
+      l1Signer: l1Provider,
+      l2Chain,
+      l2Signer: l2Provider,
+    })
+  },
+  { resolver: (l1Chain, l2Chain) => `${l1Chain.id}-${l2Chain.id}` },
+)
 
 export const watchEvmDeposit = async function (deposit: EvmDepositOperation) {
   const updates: Partial<EvmDepositOperation> = {}
 
+  const l1Chain = findChainById(deposit.l1ChainId) as Chain
+  const l2Chain = findChainById(deposit.l2ChainId) as Chain
+
+  const crossChainMessenger = await getCrossChainMessenger(l1Chain, l2Chain)
+
   // check if it has completed on the background
   const receipt = await getEvmTransactionReceipt(
     deposit.transactionHash,
@@ -13,10 +40,22 @@ export const watchEvmDeposit = async function (deposit: EvmDepositOperation) {
   if (!receipt) {
     return updates
   }
-  const newStatus =
-    receipt.status === 'success'
-      ? EvmDepositStatus.DEPOSIT_TX_CONFIRMED
-      : EvmDepositStatus.DEPOSIT_TX_FAILED
+
+  const status = await crossChainMessenger.getMessageStatus(
+    deposit.transactionHash,
+    0,
+    deposit.direction,
+  )
+
+  let newStatus: EvmDepositStatus
+  if (status === MessageStatus.RELAYED) {
+    newStatus = EvmDepositStatus.DEPOSIT_RELAYED
+  } else if (receipt.status === 'success') {
+    newStatus = EvmDepositStatus.DEPOSIT_TX_CONFIRMED
+  } else {
+    newStatus = EvmDepositStatus.DEPOSIT_TX_FAILED
+  }
+
   // if the status has changed, save the update
   if (deposit.status !== newStatus) {
     updates.status = newStatus


Reviews:
user: login: gndelia
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T18:29:29Z
user: login: gndelia
body: Do we need to update the "Status" column in the TX history to consider the new status?

<img width="1586" alt="image" src="https://github.com/user-attachments/assets/a2c086cc-50f8-45df-8d2c-811a5036c078" />

I think we do... The Action should remain the same though ("View" button)
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T18:31:02Z
user: login: gndelia
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T18:37:06Z
user: login: ArturDolzan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T20:51:45Z
user: login: ArturDolzan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T20:53:49Z
user: login: gabmontes
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T21:22:58Z
user: login: gndelia
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T21:32:07Z
user: login: ArturDolzan
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T21:32:30Z
user: login: gabmontes
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T21:38:21Z
user: login: gndelia
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T21:38:41Z
user: login: gabmontes
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T21:39:46Z
user: login: gndelia
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T21:54:36Z
user: login: gndelia
body: I just noticed that merging this PR  will break all past deposits  temporarily. As all deposits are in the state "DEPOSIT_TX_CONFIRMED", which is the past final state, but now, in order to be final, they need to move to the new state created as part of this PR to `DEPOSIT_RELAYED`

In the meantime, they will all appear as "wait up to 3 minutes"

<img width="1561" alt="image" src="https://github.com/user-attachments/assets/5286a9ed-4d49-4ebd-a976-35418b7ec73e" />


The good news is that it should auto-fix, as the watcher will iterate each deposit, and update the new status. That was what happened while I was typing this comment for example, and now it looks like this

<img width="1590" alt="image" src="https://github.com/user-attachments/assets/d624ee9d-d33e-44c1-b5f3-00c6981167a4" />


However, for users with many deposits, it may take a few minutes until all deposits are updated


cc @gabmontes 
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T22:06:55Z
user: login: gndelia
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T22:09:34Z
user: login: gabmontes
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T22:12:32Z
user: login: gndelia
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-04-01T22:19:36Z
user: login: gndelia
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-02T13:56:35Z
user: login: gndelia
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-02T14:45:34Z
user: login: gabmontes
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T15:11:37Z

Review comments:
diff_hunk: @@ -93,6 +93,8 @@ export const enum EvmDepositStatus {
   DEPOSIT_TX_FAILED = 4,
   // Approval failed
   APPROVAL_TX_FAILED = 5,
+  // Relayed
path: webapp/types/tunnel.ts
user: login: gndelia
body: I'll add this for clarity
```suggestion
  // Funds have been minted in Hemi
```
created_at: 2025-04-01T18:29:28Z
author_association: COLLABORATOR
diff_hunk: @@ -13,10 +40,22 @@ export const watchEvmDeposit = async function (deposit: EvmDepositOperation) {
   if (!receipt) {
     return updates
   }
-  const newStatus =
-    receipt.status === 'success'
-      ? EvmDepositStatus.DEPOSIT_TX_CONFIRMED
-      : EvmDepositStatus.DEPOSIT_TX_FAILED
+
+  const status = await crossChainMessenger.getMessageStatus(
+    deposit.transactionHash,
+    0,
+    deposit.direction,
+  )
+
+  let newStatus: EvmDepositStatus
+  if (receipt.status === 'success' && status === MessageStatus.RELAYED) {
path: webapp/utils/watch/evmDeposits.ts
user: login: gndelia
body: strictly speaking, I think we can remove this check

```suggestion
  if (status === MessageStatus.RELAYED) {
```

If `status === MessageStatus.RELAYED`, then `receipt.status === 'success'` should always be true.
created_at: 2025-04-01T18:37:05Z
author_association: COLLABORATOR
diff_hunk: @@ -93,6 +93,8 @@ export const enum EvmDepositStatus {
   DEPOSIT_TX_FAILED = 4,
   // Approval failed
   APPROVAL_TX_FAILED = 5,
+  // Relayed
path: webapp/types/tunnel.ts
user: login: ArturDolzan
body: Done.
created_at: 2025-04-01T20:51:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -13,10 +40,22 @@ export const watchEvmDeposit = async function (deposit: EvmDepositOperation) {
   if (!receipt) {
     return updates
   }
-  const newStatus =
-    receipt.status === 'success'
-      ? EvmDepositStatus.DEPOSIT_TX_CONFIRMED
-      : EvmDepositStatus.DEPOSIT_TX_FAILED
+
+  const status = await crossChainMessenger.getMessageStatus(
+    deposit.transactionHash,
+    0,
+    deposit.direction,
+  )
+
+  let newStatus: EvmDepositStatus
+  if (receipt.status === 'success' && status === MessageStatus.RELAYED) {
path: webapp/utils/watch/evmDeposits.ts
user: login: ArturDolzan
body: Indeed. Simplified. Thanks!
created_at: 2025-04-01T20:53:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,9 +1,36 @@
+import { MessageStatus } from '@eth-optimism/sdk'
+import pMemoize from 'promise-mem'
 import { EvmDepositStatus, type EvmDepositOperation } from 'types/tunnel'
+import { findChainById } from 'utils/chain'
+import { createQueuedCrossChainMessenger } from 'utils/crossChainMessenger'
 import { getEvmBlock, getEvmTransactionReceipt } from 'utils/evmApi'
+import { createProvider } from 'utils/providers'
+import { Chain } from 'viem'
+
+const getCrossChainMessenger = pMemoize(
path: webapp/utils/watch/evmDeposits.ts
user: login: gabmontes
body: What are we going to do with this? I know we discussed this over Slack but what PR should we merge #1102 first or what?

In either case, we should keep one of the PRs in draft and review its status after the other is merged.
created_at: 2025-04-01T21:22:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,9 +1,36 @@
+import { MessageStatus } from '@eth-optimism/sdk'
+import pMemoize from 'promise-mem'
 import { EvmDepositStatus, type EvmDepositOperation } from 'types/tunnel'
+import { findChainById } from 'utils/chain'
+import { createQueuedCrossChainMessenger } from 'utils/crossChainMessenger'
 import { getEvmBlock, getEvmTransactionReceipt } from 'utils/evmApi'
+import { createProvider } from 'utils/providers'
+import { Chain } from 'viem'
+
+const getCrossChainMessenger = pMemoize(
path: webapp/utils/watch/evmDeposits.ts
user: login: gndelia
body: I told artur to merge it as this feature is valuable, and I'd only have to rewrite it here
created_at: 2025-04-01T21:32:07Z
author_association: COLLABORATOR
diff_hunk: @@ -1,9 +1,36 @@
+import { MessageStatus } from '@eth-optimism/sdk'
+import pMemoize from 'promise-mem'
 import { EvmDepositStatus, type EvmDepositOperation } from 'types/tunnel'
+import { findChainById } from 'utils/chain'
+import { createQueuedCrossChainMessenger } from 'utils/crossChainMessenger'
 import { getEvmBlock, getEvmTransactionReceipt } from 'utils/evmApi'
+import { createProvider } from 'utils/providers'
+import { Chain } from 'viem'
+
+const getCrossChainMessenger = pMemoize(
path: webapp/utils/watch/evmDeposits.ts
user: login: ArturDolzan
body: Sure, I can keep this PR open (as a draft, like you mentioned) until we have the new way to retrieve this data. The functionality should remain the same either way, so I’m good with whatever works best for you.
created_at: 2025-04-01T21:32:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,9 +1,36 @@
+import { MessageStatus } from '@eth-optimism/sdk'
+import pMemoize from 'promise-mem'
 import { EvmDepositStatus, type EvmDepositOperation } from 'types/tunnel'
+import { findChainById } from 'utils/chain'
+import { createQueuedCrossChainMessenger } from 'utils/crossChainMessenger'
 import { getEvmBlock, getEvmTransactionReceipt } from 'utils/evmApi'
+import { createProvider } from 'utils/providers'
+import { Chain } from 'viem'
+
+const getCrossChainMessenger = pMemoize(
path: webapp/utils/watch/evmDeposits.ts
user: login: gabmontes
body: @gndelia @ArturDolzan are we updating this call right after the other PR is merged? If not or if we have the slightest chance of forgetting about this, let's open an issue and add a comment here with a link to it.
created_at: 2025-04-01T21:38:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,9 +1,36 @@
+import { MessageStatus } from '@eth-optimism/sdk'
+import pMemoize from 'promise-mem'
 import { EvmDepositStatus, type EvmDepositOperation } from 'types/tunnel'
+import { findChainById } from 'utils/chain'
+import { createQueuedCrossChainMessenger } from 'utils/crossChainMessenger'
 import { getEvmBlock, getEvmTransactionReceipt } from 'utils/evmApi'
+import { createProvider } from 'utils/providers'
+import { Chain } from 'viem'
+
+const getCrossChainMessenger = pMemoize(
path: webapp/utils/watch/evmDeposits.ts
user: login: gndelia
body: > I told artur to merge it as this feature is valuable, and I'd only have to rewrite it here
To expand on this:

That PR replaces all usages with `getWithdrawalStatus`, but here, we need something like `getDepositStatus`. It is a different combination of functions in `viem`, and I need to implement it. And the first (and only) usage would be here so... let's better merge this first, so I can rework it after (and the feature becomes available)
created_at: 2025-04-01T21:38:41Z
author_association: COLLABORATOR
diff_hunk: @@ -1,9 +1,36 @@
+import { MessageStatus } from '@eth-optimism/sdk'
+import pMemoize from 'promise-mem'
 import { EvmDepositStatus, type EvmDepositOperation } from 'types/tunnel'
+import { findChainById } from 'utils/chain'
+import { createQueuedCrossChainMessenger } from 'utils/crossChainMessenger'
 import { getEvmBlock, getEvmTransactionReceipt } from 'utils/evmApi'
+import { createProvider } from 'utils/providers'
+import { Chain } from 'viem'
+
+const getCrossChainMessenger = pMemoize(
path: webapp/utils/watch/evmDeposits.ts
user: login: gabmontes
body: Do we have an issue for that?
created_at: 2025-04-01T21:39:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,9 +1,36 @@
+import { MessageStatus } from '@eth-optimism/sdk'
+import pMemoize from 'promise-mem'
 import { EvmDepositStatus, type EvmDepositOperation } from 'types/tunnel'
+import { findChainById } from 'utils/chain'
+import { createQueuedCrossChainMessenger } from 'utils/crossChainMessenger'
 import { getEvmBlock, getEvmTransactionReceipt } from 'utils/evmApi'
+import { createProvider } from 'utils/providers'
+import { Chain } from 'viem'
+
+const getCrossChainMessenger = pMemoize(
path: webapp/utils/watch/evmDeposits.ts
user: login: gndelia
body: yes, I'm doing this after this gets merged, but regardless, and replacing the whole `eth-optimism/sdk` as part of https://github.com/hemilabs/ui-monorepo/issues/37
created_at: 2025-04-01T21:54:36Z
author_association: COLLABORATOR
diff_hunk: @@ -1,9 +1,36 @@
+import { MessageStatus } from '@eth-optimism/sdk'
+import pMemoize from 'promise-mem'
 import { EvmDepositStatus, type EvmDepositOperation } from 'types/tunnel'
+import { findChainById } from 'utils/chain'
+import { createQueuedCrossChainMessenger } from 'utils/crossChainMessenger'
 import { getEvmBlock, getEvmTransactionReceipt } from 'utils/evmApi'
+import { createProvider } from 'utils/providers'
+import { Chain } from 'viem'
+
+const getCrossChainMessenger = pMemoize(
path: webapp/utils/watch/evmDeposits.ts
user: login: gndelia
body: @gabmontes @ArturDolzan another option is me sending a commit to this PR. We'll have to coordinate a bit though
created_at: 2025-04-01T22:09:34Z
author_association: COLLABORATOR
diff_hunk: @@ -1,9 +1,36 @@
+import { MessageStatus } from '@eth-optimism/sdk'
+import pMemoize from 'promise-mem'
 import { EvmDepositStatus, type EvmDepositOperation } from 'types/tunnel'
+import { findChainById } from 'utils/chain'
+import { createQueuedCrossChainMessenger } from 'utils/crossChainMessenger'
 import { getEvmBlock, getEvmTransactionReceipt } from 'utils/evmApi'
+import { createProvider } from 'utils/providers'
+import { Chain } from 'viem'
+
+const getCrossChainMessenger = pMemoize(
path: webapp/utils/watch/evmDeposits.ts
user: login: gabmontes
body: If you guys don't forget, I'm ok.
created_at: 2025-04-01T22:12:32Z
author_association: CONTRIBUTOR

PR comments:
user: login: gndelia
created_at: 2025-04-01T18:38:43Z
body: I left some suggestions. I haven't tested it, but from the video it looks really good to me!
author_association: COLLABORATOR
user: login: ArturDolzan
created_at: 2025-04-01T21:30:16Z
body: > Do we need to update the "Status" column in the TX history to consider the new status?
> 
> <img alt="image" width="1586" src="https://private-user-images.githubusercontent.com/352474/429175309-a2c086cc-50f8-45df-8d2c-811a5036c078.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDM1NDIyMjYsIm5iZiI6MTc0MzU0MTkyNiwicGF0aCI6Ii8zNTI0NzQvNDI5MTc1MzA5LWEyYzA4NmNjLTUwZjgtNDVkZi04ZDJjLTgxMWE1MDM2YzA3OC5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwNDAxJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDQwMVQyMTEyMDZaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1mMTUyMzU3OWQxMjdkZDY4YWU0OWE3ZTYzNDBmMWEwZTQxOTlmN2IwMGM2NjAzMzRkNDdkN2I3NjVhYWFmMTZhJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.3XykjPvruhfsbipssARXj4XmkyNNc1IqKh1sacezKFQ">
> I think we do... The Action should remain the same though ("View" button)

@gndelia, I was wondering to split in different PR's however as you mentioned, implemented to reflect the properly status at the transaction history table. Please check the video:

https://github.com/user-attachments/assets/c0e8a838-82d9-4087-854d-9a7609b16476


author_association: CONTRIBUTOR
user: login: gabmontes
created_at: 2025-04-01T22:14:28Z
body: @gndelia I'm ok with this temporary auto-fixable glitch. If there is no safe workaround, let's keep it simple and let it auto fix itself.
author_association: CONTRIBUTOR
user: login: gndelia
created_at: 2025-04-02T12:59:21Z
body: @ArturDolzan  conflicts!
author_association: COLLABORATOR
user: login: ArturDolzan
created_at: 2025-04-02T13:23:17Z
body: > @ArturDolzan conflicts!

Rebased. Thanks!
author_association: CONTRIBUTOR
user: login: gndelia
created_at: 2025-04-02T13:28:29Z
body: I'm sorry, you will need to rebase this after merging #1111  😓 
author_association: COLLABORATOR