Repository: safe-global/safe-wallet-monorepo
Pull request #5382: Feat(Signing UX): show hashes for ledger
Author: login: katspaugh
Created: 2025-03-18T08:44:27Z | Merged: 2025-03-18T11:20:54Z

## What it solves

Resolves #5301

* Adjust the info steps to include a link to Safe Utils (and remove this link from the previous screen)
* Add a link to the signing article
* For hardware wallets, including Ledger Live, show hashes and an info step about them

<img width="557" alt="Screenshot 2025-03-18 at 09 44 19" src="https://github.com/user-attachments/assets/b6e4361f-7aa7-4301-83c6-7e6490a5189d" />


Linked issue #5301: [Signing UX] Show hashes for Ledger
When connected with Ledger, show safeTxHash, message hash and domain hash + a link to the openzepplin tool.

Diff:
diff --git a/apps/web/src/components/tx/ConfirmTxDetails/TxDetails.tsx b/apps/web/src/components/tx/ConfirmTxDetails/TxDetails.tsx
index 3ee204ad359..4b3af3df03a 100644
--- a/apps/web/src/components/tx/ConfirmTxDetails/TxDetails.tsx
+++ b/apps/web/src/components/tx/ConfirmTxDetails/TxDetails.tsx
@@ -9,10 +9,12 @@ import type { ReactElement, ReactNode } from 'react'
 import { isNumber, isString } from 'lodash'
 import { Operation, type TransactionData } from '@safe-global/safe-gateway-typescript-sdk/dist/types/transactions'
 import { HexEncodedData } from '@/components/transactions/HexEncodedData'
+import { SafeTxHashDataRow } from '@/components/transactions/TxDetails/Summary/SafeTxHashDataRow'
 
 type TxDetailsProps = {
   safeTx: SafeTransaction
   txData?: TransactionData
+  showHashes: boolean
 }
 
 const TxDetailsRow = ({
@@ -38,7 +40,7 @@ const TxDetailsRow = ({
   </Stack>
 )
 
-export const TxDetails = ({ safeTx, txData }: TxDetailsProps) => {
+export const TxDetails = ({ safeTx, txData, showHashes }: TxDetailsProps) => {
   const toInfo = txData?.addressInfoIndex?.[safeTx.data.to] || txData?.to
   const toName = toInfo?.name || (toInfo && 'displayName' in toInfo ? String(toInfo.displayName || '') : undefined)
   const toLogo = toInfo?.logoUri
@@ -62,7 +64,17 @@ export const TxDetails = ({ safeTx, txData }: TxDetailsProps) => {
               <Divider sx={{ mb: 1 }} />
 
               <Stack spacing={1} divider={<Divider />}>
-                <TxDetailsRow label="Primary type">SafeTx</TxDetailsRow>
+                {showHashes && (
+                  <Typography
+                    variant="body2"
+                    sx={{
+                      '& p': { color: 'text.secondary', fontSize: '14px' },
+                      '& div': { flexWrap: 'wrap', width: '100%' },
+                    }}
+                  >
+                    <SafeTxHashDataRow safeTxData={safeTx.data} />
+                  </Typography>
+                )}
 
                 <TxDetailsRow label="To">
                   {toName || toLogo ? (
diff --git a/apps/web/src/components/tx/ConfirmTxDetails/index.tsx b/apps/web/src/components/tx/ConfirmTxDetails/index.tsx
index 561e8842777..5b417144a96 100644
--- a/apps/web/src/components/tx/ConfirmTxDetails/index.tsx
+++ b/apps/web/src/components/tx/ConfirmTxDetails/index.tsx
@@ -3,38 +3,79 @@ import { Checkbox, Divider, FormControlLabel, Grid2 as Grid, Stack, StepIcon, Ty
 import commonCss from '@/components/tx-flow/common/styles.module.css'
 import { TxDetails } from './TxDetails'
 import ExternalLink from '@/components/common/ExternalLink'
-import { useContext, useState } from 'react'
+import { useCallback, useContext, useState } from 'react'
 import { SafeTxContext } from '@/components/tx-flow/SafeTxProvider'
 import SignOrExecuteFormV2 from '../SignOrExecuteForm/SignOrExecuteFormV2'
 import type { SignOrExecuteProps } from '../SignOrExecuteForm/SignOrExecuteFormV2'
 import useTxPreview from '../confirmation-views/useTxPreview'
+import Track from '@/components/common/Track'
+import { MODALS_EVENTS } from '@/services/analytics'
+import useWallet from '@/hooks/wallets/useWallet'
+import { isHardwareWallet, isLedgerLive } from '@/utils/wallets'
+
+const InfoSteps = [
+  {
+    label: 'Review what you will sign',
+    description: (
+      <Typography>
+        Signing is an irreversible action so make sure you know what you are signing.{' '}
+        <Track {...MODALS_EVENTS.SIGNING_ARTICLE}>
+          <ExternalLink href="https://help.safe.global/en/articles/276343-how-to-perform-basic-transactions-checks-on-safe-wallet">
+            Read more
+          </ExternalLink>
+        </Track>
+        .
+      </Typography>
+    ),
+  },
+  {
+    label: 'Compare with your wallet',
+    description: (
+      <Typography>
+        Once you click <b>Sign</b>, the transaction will appear in your signing wallet. Make sure that all the details
+        match.
+      </Typography>
+    ),
+  },
+  {
+    label: 'Verify with external tools',
+    description: (
+      <Typography>
+        You can additionally cross-verify your transaction data in a third-party tool like{' '}
+        <Track {...MODALS_EVENTS.OPEN_SAFE_UTILS}>
+          <ExternalLink href="https://safeutils.openzeppelin.com/">Safe Utils</ExternalLink>
+        </Track>
+        .
+      </Typography>
+    ),
+  },
+]
+
+const HardwareWalletStep = [
+  InfoSteps[1],
+  {
+    label: 'Compare with your device',
+    description: (
+      <Typography>
+        If you&apos;re using a hardware wallet with &ldquo;blind signing&rdquo;, please compare what you see on your
+        device with the hashes on the right.
+      </Typography>
+    ),
+  },
+  InfoSteps[2],
+]
 
 export const ConfirmTxDetails = (props: SignOrExecuteProps) => {
   const { safeTx, txOrigin } = useContext(SafeTxContext)
   const [txPreview] = useTxPreview(safeTx?.data)
   const [checked, setChecked] = useState(false)
+  const wallet = useWallet()
+  const showHashes = wallet ? isHardwareWallet(wallet) || isLedgerLive(wallet) : false
+  const steps = showHashes ? HardwareWalletStep : InfoSteps
 
-  const steps = [
-    {
-      label: 'Review what you will sign',
-      description: (
-        <Typography>Signing is an irreversible action so make sure you know what you are signing.</Typography>
-      ),
-    },
-    {
-      label: 'Compare with your signing wallet',
-      description: (
-        <Typography>
-          Once you click <b>Sign</b>, the transaction will appear in your signing wallet. Make sure that all the details
-          match.
-        </Typography>
-      ),
-    },
-  ]
-
-  const handleCheckboxChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+  const handleCheckboxChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
     setChecked(event.target.checked)
-  }
+  }, [])
 
   if (!safeTx) {
     return null
@@ -44,7 +85,7 @@ export const ConfirmTxDetails = (props: SignOrExecuteProps) => {
     <TxCard>
       <Grid container spacing={2}>
         <Grid size={{ xs: 12, sm: 6 }}>
-          <Stack ml={1} spacing={6}>
+          <Stack px={1} gap={6}>
             {steps.map(({ label, description }, index) => (
               <Stack key={index} spacing={2} direction="row">
                 <StepIcon icon={index + 1} active />
@@ -57,7 +98,7 @@ export const ConfirmTxDetails = (props: SignOrExecuteProps) => {
           </Stack>
         </Grid>
         <Grid size={{ xs: 12, sm: 6 }}>
-          <TxDetails safeTx={safeTx} txData={txPreview?.txData} />
+          <TxDetails safeTx={safeTx} txData={txPreview?.txData} showHashes={showHashes} />
         </Grid>
       </Grid>
 
@@ -66,14 +107,7 @@ export const ConfirmTxDetails = (props: SignOrExecuteProps) => {
       <FormControlLabel
         sx={{ mt: 2 }}
         control={<Checkbox checked={checked} onChange={handleCheckboxChange} />}
-        label={
-          <>
-            I understand what I will sign and know how to{' '}
-            <ExternalLink href="https://help.safe.global/en/articles/276343-how-to-perform-basic-transactions-checks-on-safe-wallet">
-              perform basic transaction checks.
-            </ExternalLink>
-          </>
-        }
+        label="I understand what I'm signing and understand that this is an irreversible action."
       />
 
       <SignOrExecuteFormV2
diff --git a/apps/web/src/components/tx/ReviewTransaction/__tests__/__snapshots__/index.test.tsx.snap b/apps/web/src/components/tx/ReviewTransaction/__tests__/__snapshots__/index.test.tsx.snap
index 7c8ea5023d5..fcda8774883 100644
--- a/apps/web/src/components/tx/ReviewTransaction/__tests__/__snapshots__/index.test.tsx.snap
+++ b/apps/web/src/components/tx/ReviewTransaction/__tests__/__snapshots__/index.test.tsx.snap
@@ -356,61 +356,6 @@ exports[`ReviewTransaction should display a confirmation screen 1`] = `
       >
         Transaction checks
       </h5>
-      <div
-        class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation0 MuiAlert-root MuiAlert-colorInfo MuiAlert-standardInfo MuiAlert-standard css-c7us71-MuiPaper-root-MuiAlert-root"
-        role="alert"
-        style="--Paper-shadow: none;"
-      >
-        <div
-          class="MuiAlert-icon css-vab54s-MuiAlert-icon"
-        >
-          <svg
-            aria-hidden="true"
-            class="MuiSvgIcon-root MuiSvgIcon-fontSizeInherit css-zysrwz-MuiSvgIcon-root"
-            data-testid="InfoOutlinedIcon"
-            focusable="false"
-            viewBox="0 0 24 24"
-          >
-            <path
-              d="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20, 12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10, 10 0 0,0 12,2M11,17H13V11H11V17Z"
-            />
-          </svg>
-        </div>
-        <div
-          class="MuiAlert-message css-zioonp-MuiAlert-message"
-        >
-          We recommend analyzing your transaction with a third-party tool like
-           
-          <span
-            data-track="modals: Open Safe Utils"
-          >
-            <a
-              class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways css-r9pq5a-MuiTypography-root-MuiLink-root"
-              href="https://safeutils.openzeppelin.com/"
-              rel="noreferrer noopener"
-              target="_blank"
-            >
-              <span
-                class="MuiBox-root css-1vqf8mi"
-              >
-                Safe Utils
-                <svg
-                  aria-hidden="true"
-                  class="MuiSvgIcon-root MuiSvgIcon-fontSizeSmall css-tqxw8e-MuiSvgIcon-root"
-                  data-testid="OpenInNewRoundedIcon"
-                  focusable="false"
-                  viewBox="0 0 24 24"
-                >
-                  <path
-                    d="M18 19H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h5c.55 0 1-.45 1-1s-.45-1-1-1H5c-1.11 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-6c0-.55-.45-1-1-1s-1 .45-1 1v5c0 .55-.45 1-1 1M14 4c0 .55.45 1 1 1h2.59l-9.13 9.13c-.39.39-.39 1.02 0 1.41s1.02.39 1.41 0L19 6.41V9c0 .55.45 1 1 1s1-.45 1-1V4c0-.55-.45-1-1-1h-5c-.55 0-1 .45-1 1"
-                  />
-                </svg>
-              </span>
-            </a>
-          </span>
-          . Third-party terms & disclaimers may apply.
-        </div>
-      </div>
     </div>
   </div>
   <div
@@ -640,61 +585,6 @@ exports[`ReviewTransaction should display an error screen 1`] = `
       >
         Transaction checks
       </h5>
-      <div
-        class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation0 MuiAlert-root MuiAlert-colorInfo MuiAlert-standardInfo MuiAlert-standard css-c7us71-MuiPaper-root-MuiAlert-root"
-        role="alert"
-        style="--Paper-shadow: none;"
-      >
-        <div
-          class="MuiAlert-icon css-vab54s-MuiAlert-icon"
-        >
-          <svg
-            aria-hidden="true"
-            class="MuiSvgIcon-root MuiSvgIcon-fontSizeInherit css-zysrwz-MuiSvgIcon-root"
-            data-testid="InfoOutlinedIcon"
-            focusable="false"
-            viewBox="0 0 24 24"
-          >
-            <path
-              d="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20, 12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10, 10 0 0,0 12,2M11,17H13V11H11V17Z"
-            />
-          </svg>
-        </div>
-        <div
-          class="MuiAlert-message css-zioonp-MuiAlert-message"
-        >
-          We recommend analyzing your transaction with a third-party tool like
-           
-          <span
-            data-track="modals: Open Safe Utils"
-          >
-            <a
-              class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways css-r9pq5a-MuiTypography-root-MuiLink-root"
-              href="https://safeutils.openzeppelin.com/"
-              rel="noreferrer noopener"
-              target="_blank"
-            >
-              <span
-                class="MuiBox-root css-1vqf8mi"
-              >
-                Safe Utils
-                <svg
-                  aria-hidden="true"
-                  class="MuiSvgIcon-root MuiSvgIcon-fontSizeSmall css-tqxw8e-MuiSvgIcon-root"
-                  data-testid="OpenInNewRoundedIcon"
-                  focusable="false"
-                  viewBox="0 0 24 24"
-                >
-                  <path
-                    d="M18 19H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h5c.55 0 1-.45 1-1s-.45-1-1-1H5c-1.11 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-6c0-.55-.45-1-1-1s-1 .45-1 1v5c0 .55-.45 1-1 1M14 4c0 .55.45 1 1 1h2.59l-9.13 9.13c-.39.39-.39 1.02 0 1.41s1.02.39 1.41 0L19 6.41V9c0 .55.45 1 1 1s1-.45 1-1V4c0-.55-.45-1-1-1h-5c-.55 0-1 .45-1 1"
-                  />
-                </svg>
-              </span>
-            </a>
-          </span>
-          . Third-party terms & disclaimers may apply.
-        </div>
-      </div>
     </div>
   </div>
   <div
diff --git a/apps/web/src/components/tx/SignOrExecuteForm/TxChecks.tsx b/apps/web/src/components/tx/SignOrExecuteForm/TxChecks.tsx
index 0cf99457463..80f8a07f4ec 100644
--- a/apps/web/src/components/tx/SignOrExecuteForm/TxChecks.tsx
+++ b/apps/web/src/components/tx/SignOrExecuteForm/TxChecks.tsx
@@ -4,16 +4,12 @@ import { FEATURES } from '@/utils/chains'
 import { type ReactElement } from 'react'
 import { TxSimulation, TxSimulationMessage } from '@/components/tx/security/tenderly'
 import TxCard from '@/components/tx-flow/common/TxCard'
-import { Alert, Box, Typography } from '@mui/material'
-import ExternalLink from '@/components/common/ExternalLink'
-import Track from '@/components/common/Track'
-import { MODALS_EVENTS } from '@/services/analytics'
+import { Box, Typography } from '@mui/material'
+
 import type { SafeTransaction, MetaTransactionData } from '@safe-global/safe-core-sdk-types'
 
 import css from './styles.module.css'
 
-const SAFE_UTILS_URL = 'https://safeutils.openzeppelin.com/'
-
 const TxChecks = ({
   executionOwner,
   disabled = false,
@@ -31,14 +27,6 @@ const TxChecks = ({
     <TxCard>
       <Typography variant="h5">Transaction checks</Typography>
 
-      <Alert severity="info">
-        We recommend analyzing your transaction with a third-party tool like{' '}
-        <Track {...MODALS_EVENTS.OPEN_SAFE_UTILS}>
-          <ExternalLink href={SAFE_UTILS_URL}>Safe Utils</ExternalLink>
-        </Track>
-        . Third-party terms & disclaimers may apply.
-      </Alert>
-
       {(isTxSimulationFeatureEnabled || isRiskMitigationFeatureEnabled) && (
         <>
           <TxSimulation disabled={disabled} transactions={transaction} executionOwner={executionOwner} />
diff --git a/apps/web/src/components/tx/SignOrExecuteForm/__tests__/__snapshots__/SignOrExecute.test.tsx.snap b/apps/web/src/components/tx/SignOrExecuteForm/__tests__/__snapshots__/SignOrExecute.test.tsx.snap
index 90a88443385..3fae9a878bc 100644
--- a/apps/web/src/components/tx/SignOrExecuteForm/__tests__/__snapshots__/SignOrExecute.test.tsx.snap
+++ b/apps/web/src/components/tx/SignOrExecuteForm/__tests__/__snapshots__/SignOrExecute.test.tsx.snap
@@ -356,61 +356,6 @@ exports[`SignOrExecute should display a confirmation screen 1`] = `
       >
         Transaction checks
       </h5>
-      <div
-        class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation0 MuiAlert-root MuiAlert-colorInfo MuiAlert-standardInfo MuiAlert-standard css-c7us71-MuiPaper-root-MuiAlert-root"
-        role="alert"
-        style="--Paper-shadow: none;"
-      >
-        <div
-          class="MuiAlert-icon css-vab54s-MuiAlert-icon"
-        >
-          <svg
-            aria-hidden="true"
-            class="MuiSvgIcon-root MuiSvgIcon-fontSizeInherit css-zysrwz-MuiSvgIcon-root"
-            data-testid="InfoOutlinedIcon"
-            focusable="false"
-            viewBox="0 0 24 24"
-          >
-            <path
-              d="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20, 12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10, 10 0 0,0 12,2M11,17H13V11H11V17Z"
-            />
-          </svg>
-        </div>
-        <div
-          class="MuiAlert-message css-zioonp-MuiAlert-message"
-        >
-          We recommend analyzing your transaction with a third-party tool like
-           
-          <span
-            data-track="modals: Open Safe Utils"
-          >
-            <a
-              class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways css-r9pq5a-MuiTypography-root-MuiLink-root"
-              href="https://safeutils.openzeppelin.com/"
-              rel="noreferrer noopener"
-              target="_blank"
-            >
-              <span
-                class="MuiBox-root css-1vqf8mi"
-              >
-                Safe Utils
-                <svg
-                  aria-hidden="true"
-                  class="MuiSvgIcon-root MuiSvgIcon-fontSizeSmall css-tqxw8e-MuiSvgIcon-root"
-                  data-testid="OpenInNewRoundedIcon"
-                  focusable="false"
-                  viewBox="0 0 24 24"
-                >
-                  <path
-                    d="M18 19H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h5c.55 0 1-.45 1-1s-.45-1-1-1H5c-1.11 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-6c0-.55-.45-1-1-1s-1 .45-1 1v5c0 .55-.45 1-1 1M14 4c0 .55.45 1 1 1h2.59l-9.13 9.13c-.39.39-.39 1.02 0 1.41s1.02.39 1.41 0L19 6.41V9c0 .55.45 1 1 1s1-.45 1-1V4c0-.55-.45-1-1-1h-5c-.55 0-1 .45-1 1"
-                  />
-                </svg>
-              </span>
-            </a>
-          </span>
-          . Third-party terms & disclaimers may apply.
-        </div>
-      </div>
     </div>
   </div>
   <div
@@ -694,61 +639,6 @@ exports[`SignOrExecute should display an error screen 1`] = `
       >
         Transaction checks
       </h5>
-      <div
-        class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation0 MuiAlert-root MuiAlert-colorInfo MuiAlert-standardInfo MuiAlert-standard css-c7us71-MuiPaper-root-MuiAlert-root"
-        role="alert"
-        style="--Paper-shadow: none;"
-      >
-        <div
-          class="MuiAlert-icon css-vab54s-MuiAlert-icon"
-        >
-          <svg
-            aria-hidden="true"
-            class="MuiSvgIcon-root MuiSvgIcon-fontSizeInherit css-zysrwz-MuiSvgIcon-root"
-            data-testid="InfoOutlinedIcon"
-            focusable="false"
-            viewBox="0 0 24 24"
-          >
-            <path
-              d="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20, 12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10, 10 0 0,0 12,2M11,17H13V11H11V17Z"
-            />
-          </svg>
-        </div>
-        <div
-          class="MuiAlert-message css-zioonp-MuiAlert-message"
-        >
-          We recommend analyzing your transaction with a third-party tool like
-           
-          <span
-            data-track="modals: Open Safe Utils"
-          >
-            <a
-              class="MuiTypography-root MuiTypography-inherit MuiLink-root MuiLink-underlineAlways css-r9pq5a-MuiTypography-root-MuiLink-root"
-              href="https://safeutils.openzeppelin.com/"
-              rel="noreferrer noopener"
-              target="_blank"
-            >
-              <span
-                class="MuiBox-root css-1vqf8mi"
-              >
-                Safe Utils
-                <svg
-                  aria-hidden="true"
-                  class="MuiSvgIcon-root MuiSvgIcon-fontSizeSmall css-tqxw8e-MuiSvgIcon-root"
-                  data-testid="OpenInNewRoundedIcon"
-                  focusable="false"
-                  viewBox="0 0 24 24"
-                >
-                  <path
-                    d="M18 19H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h5c.55 0 1-.45 1-1s-.45-1-1-1H5c-1.11 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-6c0-.55-.45-1-1-1s-1 .45-1 1v5c0 .55-.45 1-1 1M14 4c0 .55.45 1 1 1h2.59l-9.13 9.13c-.39.39-.39 1.02 0 1.41s1.02.39 1.41 0L19 6.41V9c0 .55.45 1 1 1s1-.45 1-1V4c0-.55-.45-1-1-1h-5c-.55 0-1 .45-1 1"
-                  />
-                </svg>
-              </span>
-            </a>
-          </span>
-          . Third-party terms & disclaimers may apply.
-        </div>
-      </div>
     </div>
   </div>
   <div
diff --git a/apps/web/src/hooks/wallets/useOnboard.ts b/apps/web/src/hooks/wallets/useOnboard.ts
index b73596fface..9d581c2a938 100644
--- a/apps/web/src/hooks/wallets/useOnboard.ts
+++ b/apps/web/src/hooks/wallets/useOnboard.ts
@@ -79,7 +79,7 @@ export const getConnectedWallet = (wallets: WalletState[]): ConnectedWallet | nu
   }
 }
 
-const getWalletConnectLabel = async (wallet: ConnectedWallet): Promise<string | undefined> => {
+export const getWalletConnectLabel = (wallet: ConnectedWallet): string | undefined => {
   const UNKNOWN_PEER = 'Unknown'
   if (!isWalletConnect(wallet)) return
   const { connector } = wallet.provider as unknown as any
@@ -90,16 +90,13 @@ const getWalletConnectLabel = async (wallet: ConnectedWallet): Promise<string |
 const trackWalletType = (wallet: ConnectedWallet) => {
   trackEvent({ ...WALLET_EVENTS.CONNECT, label: wallet.label })
 
-  getWalletConnectLabel(wallet)
-    .then((wcLabel) => {
-      if (wcLabel) {
-        trackEvent({
-          ...WALLET_EVENTS.WALLET_CONNECT,
-          label: wcLabel,
-        })
-      }
+  const wcLabel = getWalletConnectLabel(wallet)
+  if (wcLabel) {
+    trackEvent({
+      ...WALLET_EVENTS.WALLET_CONNECT,
+      label: wcLabel,
     })
-    .catch(() => null)
+  }
 }
 
 let isConnecting = false
diff --git a/apps/web/src/services/analytics/events/modals.ts b/apps/web/src/services/analytics/events/modals.ts
index 149e89a2e6a..ed40b485d2c 100644
--- a/apps/web/src/services/analytics/events/modals.ts
+++ b/apps/web/src/services/analytics/events/modals.ts
@@ -40,6 +40,10 @@ export const MODALS_EVENTS = {
     action: 'Open Safe Utils',
     category: MODALS_CATEGORY,
   },
+  SIGNING_ARTICLE: {
+    action: 'Open signing article',
+    category: MODALS_CATEGORY,
+  },
   SIMULATE_TX: {
     action: 'Simulate transaction',
     category: MODALS_CATEGORY,
diff --git a/apps/web/src/utils/wallets.ts b/apps/web/src/utils/wallets.ts
index 9596de55d96..05dd9388d60 100644
--- a/apps/web/src/utils/wallets.ts
+++ b/apps/web/src/utils/wallets.ts
@@ -1,5 +1,5 @@
 import type { EthersError } from '@/utils/ethers-utils'
-import { type ConnectedWallet } from '@/hooks/wallets/useOnboard'
+import { getWalletConnectLabel, type ConnectedWallet } from '@/hooks/wallets/useOnboard'
 import { getWeb3ReadOnly } from '@/hooks/wallets/web3'
 import { WALLET_KEYS } from '@/hooks/wallets/consts'
 import { EMPTY_DATA } from '@safe-global/protocol-kit/dist/src/utils/constants'
@@ -8,6 +8,7 @@ import { PRIVATE_KEY_MODULE_LABEL } from '@/services/private-key-module'
 import { type JsonRpcProvider } from 'ethers'
 
 const WALLETCONNECT = 'WalletConnect'
+const WC_LEDGER = 'Ledger'
 
 const isWCRejection = (err: Error): boolean => {
   return /rejected/.test(err?.message)
@@ -21,8 +22,12 @@ export const isWalletRejection = (err: EthersError | Error): boolean => {
   return isEthersRejection(err as EthersError) || isWCRejection(err)
 }
 
+export const isLedgerLive = (wallet: ConnectedWallet): boolean => {
+  return getWalletConnectLabel(wallet) === WC_LEDGER
+}
+
 export const isLedger = (wallet: ConnectedWallet): boolean => {
-  return wallet.label.toUpperCase() === WALLET_KEYS.LEDGER
+  return wallet.label.toUpperCase() === WALLET_KEYS.LEDGER || isLedgerLive(wallet)
 }
 
 export const isWalletConnect = (wallet: ConnectedWallet): boolean => {


Reviews:
user: login: iamacook
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T08:59:20Z
user: login: katspaugh
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-18T09:18:03Z
user: login: katspaugh
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-18T09:18:56Z
user: login: iamacook
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T09:28:50Z
user: login: iamacook
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T09:28:56Z
user: login: iamacook
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T11:18:00Z

Review comments:
diff_hunk: @@ -3,38 +3,79 @@ import { Checkbox, Divider, FormControlLabel, Grid2 as Grid, Stack, StepIcon, Ty
 import commonCss from '@/components/tx-flow/common/styles.module.css'
 import { TxDetails } from './TxDetails'
 import ExternalLink from '@/components/common/ExternalLink'
-import { useContext, useState } from 'react'
+import { useCallback, useContext, useState } from 'react'
 import { SafeTxContext } from '@/components/tx-flow/SafeTxProvider'
 import SignOrExecuteFormV2 from '../SignOrExecuteForm/SignOrExecuteFormV2'
 import type { SignOrExecuteProps } from '../SignOrExecuteForm/SignOrExecuteFormV2'
 import useTxPreview from '../confirmation-views/useTxPreview'
+import Track from '@/components/common/Track'
+import { MODALS_EVENTS } from '@/services/analytics'
+import useWallet from '@/hooks/wallets/useWallet'
+import { isHardwareWallet, isLedgerLive } from '@/utils/wallets'
+
+const InfoSteps = [
+  {
+    label: 'Review what you will sign',
+    description: (
+      <Typography>
+        Signing is an irreversible action so make sure you know what you are signing.{' '}
+        <Track {...MODALS_EVENTS.SIGNING_ARTICLE}>
+          <ExternalLink href="https://help.safe.global/en/articles/276343-how-to-perform-basic-transactions-checks-on-safe-wallet">
+            Read more
+          </ExternalLink>
+        </Track>
+        .
+      </Typography>
+    ),
+  },
+  {
+    label: 'Compare with your wallet',
+    description: (
+      <Typography>
+        Once you click <b>Sign</b>, the transaction will appear in your signing wallet. Make sure that all the details
+        match.
+      </Typography>
+    ),
+  },
+  {
+    label: 'Verify with external tools',
+    description: (
+      <Typography>
+        You can additionally cross-verify your transaction data in a third-party tool like{' '}
+        <Track {...MODALS_EVENTS.OPEN_SAFE_UTILS}>
+          <ExternalLink href="https://safeutils.openzeppelin.com/">Safe Utils</ExternalLink>
+        </Track>
+        .
+      </Typography>
+    ),
+  },
+]
+
+const HardwareWalletStep = [
+  InfoSteps[1],
+  {
+    label: 'For hardware wallets',
+    description: (
+      <Typography>
+        If you&apos;re using a hardware wallet with "blind" signing, please compare what you see on your device with the
path: apps/web/src/components/tx/ConfirmTxDetails/index.tsx
user: login: iamacook
body: ```suggestion
        If you&apos;re using a hardware wallet with "blind signing", please compare what you see on your device with the
```
created_at: 2025-03-18T08:48:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3,38 +3,79 @@ import { Checkbox, Divider, FormControlLabel, Grid2 as Grid, Stack, StepIcon, Ty
 import commonCss from '@/components/tx-flow/common/styles.module.css'
 import { TxDetails } from './TxDetails'
 import ExternalLink from '@/components/common/ExternalLink'
-import { useContext, useState } from 'react'
+import { useCallback, useContext, useState } from 'react'
 import { SafeTxContext } from '@/components/tx-flow/SafeTxProvider'
 import SignOrExecuteFormV2 from '../SignOrExecuteForm/SignOrExecuteFormV2'
 import type { SignOrExecuteProps } from '../SignOrExecuteForm/SignOrExecuteFormV2'
 import useTxPreview from '../confirmation-views/useTxPreview'
+import Track from '@/components/common/Track'
+import { MODALS_EVENTS } from '@/services/analytics'
+import useWallet from '@/hooks/wallets/useWallet'
+import { isHardwareWallet, isLedgerLive } from '@/utils/wallets'
+
+const InfoSteps = [
+  {
+    label: 'Review what you will sign',
+    description: (
+      <Typography>
+        Signing is an irreversible action so make sure you know what you are signing.{' '}
+        <Track {...MODALS_EVENTS.SIGNING_ARTICLE}>
+          <ExternalLink href="https://help.safe.global/en/articles/276343-how-to-perform-basic-transactions-checks-on-safe-wallet">
+            Read more
+          </ExternalLink>
+        </Track>
+        .
+      </Typography>
+    ),
+  },
+  {
+    label: 'Compare with your wallet',
+    description: (
+      <Typography>
+        Once you click <b>Sign</b>, the transaction will appear in your signing wallet. Make sure that all the details
+        match.
+      </Typography>
+    ),
+  },
+  {
+    label: 'Verify with external tools',
+    description: (
+      <Typography>
+        You can additionally cross-verify your transaction data in a third-party tool like{' '}
+        <Track {...MODALS_EVENTS.OPEN_SAFE_UTILS}>
+          <ExternalLink href="https://safeutils.openzeppelin.com/">Safe Utils</ExternalLink>
+        </Track>
+        .
+      </Typography>
+    ),
+  },
+]
+
+const HardwareWalletStep = [
+  InfoSteps[1],
+  {
+    label: 'For hardware wallets',
+    description: (
+      <Typography>
+        If you&apos;re using a hardware wallet with "blind" signing, please compare what you see on your device with the
+        hashes on the right.
+      </Typography>
+    ),
+  },
+  InfoSteps[2],
+]
 
 export const ConfirmTxDetails = (props: SignOrExecuteProps) => {
   const { safeTx, txOrigin } = useContext(SafeTxContext)
   const [txPreview] = useTxPreview(safeTx?.data)
   const [checked, setChecked] = useState(false)
+  const wallet = useWallet()
+  const showHashes = wallet ? isHardwareWallet(wallet) || isLedgerLive(wallet) : false
path: apps/web/src/components/tx/ConfirmTxDetails/index.tsx
user: login: iamacook
body: Without the `SafeTx`, it's not possible to compute the necessary hashes, making external verification impossible.

A couple instances that come to mind:
1. On-chain confirmations: The `safeTxHash` (which cannot be computed) is signed.
2. Ledger Live: The interface displays the hashes, but the device shows the `SafeTx`.
created_at: 2025-03-18T08:50:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3,38 +3,79 @@ import { Checkbox, Divider, FormControlLabel, Grid2 as Grid, Stack, StepIcon, Ty
 import commonCss from '@/components/tx-flow/common/styles.module.css'
 import { TxDetails } from './TxDetails'
 import ExternalLink from '@/components/common/ExternalLink'
-import { useContext, useState } from 'react'
+import { useCallback, useContext, useState } from 'react'
 import { SafeTxContext } from '@/components/tx-flow/SafeTxProvider'
 import SignOrExecuteFormV2 from '../SignOrExecuteForm/SignOrExecuteFormV2'
 import type { SignOrExecuteProps } from '../SignOrExecuteForm/SignOrExecuteFormV2'
 import useTxPreview from '../confirmation-views/useTxPreview'
+import Track from '@/components/common/Track'
+import { MODALS_EVENTS } from '@/services/analytics'
+import useWallet from '@/hooks/wallets/useWallet'
+import { isHardwareWallet, isLedgerLive } from '@/utils/wallets'
+
+const InfoSteps = [
+  {
+    label: 'Review what you will sign',
+    description: (
+      <Typography>
+        Signing is an irreversible action so make sure you know what you are signing.{' '}
+        <Track {...MODALS_EVENTS.SIGNING_ARTICLE}>
+          <ExternalLink href="https://help.safe.global/en/articles/276343-how-to-perform-basic-transactions-checks-on-safe-wallet">
+            Read more
+          </ExternalLink>
+        </Track>
+        .
+      </Typography>
+    ),
+  },
+  {
+    label: 'Compare with your wallet',
+    description: (
+      <Typography>
+        Once you click <b>Sign</b>, the transaction will appear in your signing wallet. Make sure that all the details
+        match.
+      </Typography>
+    ),
+  },
+  {
+    label: 'Verify with external tools',
+    description: (
+      <Typography>
+        You can additionally cross-verify your transaction data in a third-party tool like{' '}
+        <Track {...MODALS_EVENTS.OPEN_SAFE_UTILS}>
+          <ExternalLink href="https://safeutils.openzeppelin.com/">Safe Utils</ExternalLink>
+        </Track>
+        .
+      </Typography>
+    ),
+  },
+]
+
+const HardwareWalletStep = [
+  InfoSteps[1],
+  {
+    label: 'For hardware wallets',
+    description: (
+      <Typography>
+        If you&apos;re using a hardware wallet with "blind" signing, please compare what you see on your device with the
+        hashes on the right.
+      </Typography>
+    ),
+  },
+  InfoSteps[2],
+]
 
 export const ConfirmTxDetails = (props: SignOrExecuteProps) => {
   const { safeTx, txOrigin } = useContext(SafeTxContext)
   const [txPreview] = useTxPreview(safeTx?.data)
   const [checked, setChecked] = useState(false)
+  const wallet = useWallet()
+  const showHashes = wallet ? isHardwareWallet(wallet) || isLedgerLive(wallet) : false
path: apps/web/src/components/tx/ConfirmTxDetails/index.tsx
user: login: katspaugh
body: Not sure I follow, how can there be no SafeTx?
created_at: 2025-03-18T09:18:03Z
author_association: MEMBER
diff_hunk: @@ -3,38 +3,79 @@ import { Checkbox, Divider, FormControlLabel, Grid2 as Grid, Stack, StepIcon, Ty
 import commonCss from '@/components/tx-flow/common/styles.module.css'
 import { TxDetails } from './TxDetails'
 import ExternalLink from '@/components/common/ExternalLink'
-import { useContext, useState } from 'react'
+import { useCallback, useContext, useState } from 'react'
 import { SafeTxContext } from '@/components/tx-flow/SafeTxProvider'
 import SignOrExecuteFormV2 from '../SignOrExecuteForm/SignOrExecuteFormV2'
 import type { SignOrExecuteProps } from '../SignOrExecuteForm/SignOrExecuteFormV2'
 import useTxPreview from '../confirmation-views/useTxPreview'
+import Track from '@/components/common/Track'
+import { MODALS_EVENTS } from '@/services/analytics'
+import useWallet from '@/hooks/wallets/useWallet'
+import { isHardwareWallet, isLedgerLive } from '@/utils/wallets'
+
+const InfoSteps = [
+  {
+    label: 'Review what you will sign',
+    description: (
+      <Typography>
+        Signing is an irreversible action so make sure you know what you are signing.{' '}
+        <Track {...MODALS_EVENTS.SIGNING_ARTICLE}>
+          <ExternalLink href="https://help.safe.global/en/articles/276343-how-to-perform-basic-transactions-checks-on-safe-wallet">
+            Read more
+          </ExternalLink>
+        </Track>
+        .
+      </Typography>
+    ),
+  },
+  {
+    label: 'Compare with your wallet',
+    description: (
+      <Typography>
+        Once you click <b>Sign</b>, the transaction will appear in your signing wallet. Make sure that all the details
+        match.
+      </Typography>
+    ),
+  },
+  {
+    label: 'Verify with external tools',
+    description: (
+      <Typography>
+        You can additionally cross-verify your transaction data in a third-party tool like{' '}
+        <Track {...MODALS_EVENTS.OPEN_SAFE_UTILS}>
+          <ExternalLink href="https://safeutils.openzeppelin.com/">Safe Utils</ExternalLink>
+        </Track>
+        .
+      </Typography>
+    ),
+  },
+]
+
+const HardwareWalletStep = [
+  InfoSteps[1],
+  {
+    label: 'For hardware wallets',
path: apps/web/src/components/tx/ConfirmTxDetails/index.tsx
user: login: katspaugh
body: ```suggestion
    label: 'Compare with your device',
```

@iamacook wdyt?
created_at: 2025-03-18T09:18:55Z
author_association: MEMBER
diff_hunk: @@ -3,38 +3,79 @@ import { Checkbox, Divider, FormControlLabel, Grid2 as Grid, Stack, StepIcon, Ty
 import commonCss from '@/components/tx-flow/common/styles.module.css'
 import { TxDetails } from './TxDetails'
 import ExternalLink from '@/components/common/ExternalLink'
-import { useContext, useState } from 'react'
+import { useCallback, useContext, useState } from 'react'
 import { SafeTxContext } from '@/components/tx-flow/SafeTxProvider'
 import SignOrExecuteFormV2 from '../SignOrExecuteForm/SignOrExecuteFormV2'
 import type { SignOrExecuteProps } from '../SignOrExecuteForm/SignOrExecuteFormV2'
 import useTxPreview from '../confirmation-views/useTxPreview'
+import Track from '@/components/common/Track'
+import { MODALS_EVENTS } from '@/services/analytics'
+import useWallet from '@/hooks/wallets/useWallet'
+import { isHardwareWallet, isLedgerLive } from '@/utils/wallets'
+
+const InfoSteps = [
+  {
+    label: 'Review what you will sign',
+    description: (
+      <Typography>
+        Signing is an irreversible action so make sure you know what you are signing.{' '}
+        <Track {...MODALS_EVENTS.SIGNING_ARTICLE}>
+          <ExternalLink href="https://help.safe.global/en/articles/276343-how-to-perform-basic-transactions-checks-on-safe-wallet">
+            Read more
+          </ExternalLink>
+        </Track>
+        .
+      </Typography>
+    ),
+  },
+  {
+    label: 'Compare with your wallet',
+    description: (
+      <Typography>
+        Once you click <b>Sign</b>, the transaction will appear in your signing wallet. Make sure that all the details
+        match.
+      </Typography>
+    ),
+  },
+  {
+    label: 'Verify with external tools',
+    description: (
+      <Typography>
+        You can additionally cross-verify your transaction data in a third-party tool like{' '}
+        <Track {...MODALS_EVENTS.OPEN_SAFE_UTILS}>
+          <ExternalLink href="https://safeutils.openzeppelin.com/">Safe Utils</ExternalLink>
+        </Track>
+        .
+      </Typography>
+    ),
+  },
+]
+
+const HardwareWalletStep = [
+  InfoSteps[1],
+  {
+    label: 'For hardware wallets',
+    description: (
+      <Typography>
+        If you&apos;re using a hardware wallet with "blind" signing, please compare what you see on your device with the
+        hashes on the right.
+      </Typography>
+    ),
+  },
+  InfoSteps[2],
+]
 
 export const ConfirmTxDetails = (props: SignOrExecuteProps) => {
   const { safeTx, txOrigin } = useContext(SafeTxContext)
   const [txPreview] = useTxPreview(safeTx?.data)
   const [checked, setChecked] = useState(false)
+  const wallet = useWallet()
+  const showHashes = wallet ? isHardwareWallet(wallet) || isLedgerLive(wallet) : false
path: apps/web/src/components/tx/ConfirmTxDetails/index.tsx
user: login: iamacook
body: Apologies, I had my reasoning reversed.

Without the hashes, external tools cannot verify them, and the `safeTxHash` is approved in parent Safes.
created_at: 2025-03-18T09:28:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3,38 +3,79 @@ import { Checkbox, Divider, FormControlLabel, Grid2 as Grid, Stack, StepIcon, Ty
 import commonCss from '@/components/tx-flow/common/styles.module.css'
 import { TxDetails } from './TxDetails'
 import ExternalLink from '@/components/common/ExternalLink'
-import { useContext, useState } from 'react'
+import { useCallback, useContext, useState } from 'react'
 import { SafeTxContext } from '@/components/tx-flow/SafeTxProvider'
 import SignOrExecuteFormV2 from '../SignOrExecuteForm/SignOrExecuteFormV2'
 import type { SignOrExecuteProps } from '../SignOrExecuteForm/SignOrExecuteFormV2'
 import useTxPreview from '../confirmation-views/useTxPreview'
+import Track from '@/components/common/Track'
+import { MODALS_EVENTS } from '@/services/analytics'
+import useWallet from '@/hooks/wallets/useWallet'
+import { isHardwareWallet, isLedgerLive } from '@/utils/wallets'
+
+const InfoSteps = [
+  {
+    label: 'Review what you will sign',
+    description: (
+      <Typography>
+        Signing is an irreversible action so make sure you know what you are signing.{' '}
+        <Track {...MODALS_EVENTS.SIGNING_ARTICLE}>
+          <ExternalLink href="https://help.safe.global/en/articles/276343-how-to-perform-basic-transactions-checks-on-safe-wallet">
+            Read more
+          </ExternalLink>
+        </Track>
+        .
+      </Typography>
+    ),
+  },
+  {
+    label: 'Compare with your wallet',
+    description: (
+      <Typography>
+        Once you click <b>Sign</b>, the transaction will appear in your signing wallet. Make sure that all the details
+        match.
+      </Typography>
+    ),
+  },
+  {
+    label: 'Verify with external tools',
+    description: (
+      <Typography>
+        You can additionally cross-verify your transaction data in a third-party tool like{' '}
+        <Track {...MODALS_EVENTS.OPEN_SAFE_UTILS}>
+          <ExternalLink href="https://safeutils.openzeppelin.com/">Safe Utils</ExternalLink>
+        </Track>
+        .
+      </Typography>
+    ),
+  },
+]
+
+const HardwareWalletStep = [
+  InfoSteps[1],
+  {
+    label: 'For hardware wallets',
path: apps/web/src/components/tx/ConfirmTxDetails/index.tsx
user: login: iamacook
body: Sounds good to me.
created_at: 2025-03-18T09:28:56Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-18T08:44:43Z
body: <!-- add-pr-comment:praul -->

## Branch preview
✅  Deploy successful!

**Website:**
https://hashes--walletweb.review.5afe.dev/home?safe=eth:0xA77DE01e157f9f57C7c4A326eeE9C4874D0598b6

**Storybook:**
https://hashes--walletweb.review.5afe.dev/storybook/
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-18T08:52:22Z
body: <!-- jest coverage report action for options with hash 538b3aeca5435921509d9329a38101b7 -->

## Coverage report for `apps/web`


| <div title="Status of coverage:&#10; 🟢 - ok&#10; 🟡 - slightly more than threshold&#10; 🔴 - under the threshold">St.<sup>:grey_question:</sup></div> | Category | Percentage | Covered / Total |
| :-: | :- | :- | :-: |
| 🟡 | Statements | <div title="Base coverage is: 77.47%">77.76% (+0.28% 🔼)</div> | 14485/18629 |
| 🔴 | Branches | <div title="Base coverage is: 56.55%">57.3% (+0.76% 🔼)</div> | 3782/6600 |
| 🟡 | Functions | <div title="Base coverage is: 62.31%">62.62% (+0.31% 🔼)</div> | 2153/3438 |
| 🟡 | Lines | <div title="Base coverage is: 78.97%">79.24% (+0.27% 🔼)</div> | 13086/16515 |


## Test suite run success
1864 tests passing in 253 suites.

<p align="right">Report generated by <a href="https://github.com/ArtiomTr/jest-coverage-report-action">🧪jest coverage report action</a> from c16a3aa95ba42c4bc29bc09fa1477f8daa708693</p>

author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-18T09:48:15Z
body: ## 📦 Next.js Bundle Analysis for @safe-global/web

This analysis was generated by the [Next.js Bundle Analysis action](https://github.com/hashicorp/nextjs-bundle-analysis). 🤖

### ⚠️  Global Bundle Size Increased
  
Page | Size (compressed) | 
|---|---|
| `global` | `1.07 MB` _(🟡 +78.8 KB)_ |

<details>
<summary>Details</summary>
<p>The <strong>global bundle</strong> is the javascript bundle that loads alongside every page. It is in its own category because its impact is much higher - an increase to its size means that every page on your website loads slower, and a decrease means every page loads faster.</p>
<p>Any third party scripts you have added directly to your app using the <code>&lt;script&gt;</code> tag are not accounted for in this analysis</p>
<p>If you want further insight into what is behind the changes, give <a href='https://www.npmjs.com/package/@next/bundle-analyzer'>@next/bundle-analyzer</a> a try!</p>
</details>

### Thirty-five Pages Changed Size
  
The following pages changed size from the code in this PR compared to its base branch:

Page | Size (compressed) | First Load |
|---|---|---|
| `/` | `509 B` _(🟢 -1 B)_ | 1.07 MB |
| `/_offline` | `1.1 KB` _(🟢 -1 B)_ | 1.07 MB |
| `/address-book` | `23.22 KB` _(🟡 +152 B)_ | 1.09 MB |
| `/apps` | `35.94 KB` _(🟡 +2.21 KB)_ | 1.1 MB |
| `/apps/custom` | `34 KB` _(🟡 +2.22 KB)_ | 1.1 MB |
| `/apps/open` | `58.86 KB` _(🟡 +5.25 KB)_ | 1.12 MB |
| `/balances` | `29.84 KB` _(🟡 +134 B)_ | 1.09 MB |
| `/balances/nfts` | `9.31 KB` _(🟢 -235 B)_ | 1.07 MB |
| `/bridge` | `2.56 KB` _(🟡 +2 B)_ | 1.07 MB |
| `/cookie` | `8.77 KB` _(🟡 +1 B)_ | 1.07 MB |
| `/home` | `61.79 KB` _(🟡 +2.53 KB)_ | 1.13 MB |
| `/licenses` | `2.12 KB` _(🟢 -351 B)_ | 1.07 MB |
| `/new-safe/advanced-create` | `26.65 KB` _(🟡 +206 B)_ | 1.09 MB |
| `/new-safe/create` | `25.5 KB` _(🟢 -95 B)_ | 1.09 MB |
| `/new-safe/load` | `6.09 KB` _(🟡 +3 B)_ | 1.07 MB |
| `/privacy` | `14.57 KB` _(🟡 +2 B)_ | 1.08 MB |
| `/settings/cookies` | `1.87 KB` _(🟢 -2 B)_ | 1.07 MB |
| `/settings/data` | `1.79 KB` _(🟢 -3 B)_ | 1.07 MB |
| `/settings/environment-variables` | `3.27 KB` _(🟢 -1 B)_ | 1.07 MB |
| `/settings/modules` | `2.91 KB` _(🟢 -1.14 KB)_ | 1.07 MB |
| `/settings/notifications` | `7.56 KB` _(🟢 -13.76 KB)_ | 1.07 MB |
| `/settings/safe-apps` | `20.33 KB` _(🟡 +2.05 KB)_ | 1.09 MB |
| `/settings/setup` | `30.98 KB` _(🟡 +255 B)_ | 1.1 MB |
| `/share/safe-app` | `7.52 KB` _(🟢 -39 B)_ | 1.07 MB |
| `/stake` | `622 B` _(🟡 +3 B)_ | 1.07 MB |
| `/swap` | `763 B` _(🟡 +3 B)_ | 1.07 MB |
| `/terms` | `12.93 KB` _(🟡 +1 B)_ | 1.08 MB |
| `/transactions` | `103.03 KB` _(🟡 +6.46 KB)_ | 1.17 MB |
| `/transactions/history` | `102.99 KB` _(🟡 +6.45 KB)_ | 1.17 MB |
| `/transactions/messages` | `63.61 KB` _(🟡 +5.32 KB)_ | 1.13 MB |
| `/transactions/msg` | `59.87 KB` _(🟡 +5.32 KB)_ | 1.12 MB |
| `/transactions/queue` | `52.73 KB` _(🟡 +5.33 KB)_ | 1.12 MB |
| `/transactions/tx` | `52.08 KB` _(🟡 +5.32 KB)_ | 1.12 MB |
| `/welcome` | `6.93 KB` _(🟢 -2 B)_ | 1.07 MB |
| `/welcome/accounts` | `408 B` _(🟡 +1 B)_ | 1.07 MB |

<details>
<summary>Details</summary>
<p>Only the gzipped size is provided here based on <a href='https://twitter.com/slightlylate/status/1412851269211811845'>an expert tip</a>.</p>
<p><strong>First Load</strong> is the size of the global bundle plus the bundle for the individual page. If a user were to show up to your website and land on a given page, the first load size represents the amount of javascript that user would need to download. If <code>next/link</code> is used, subsequent page loads would only need to download that page's bundle (the number in the "Size" column), since the global bundle has already been downloaded.</p>
<p>Any third party scripts you have added directly to your app using the <code>&lt;script&gt;</code> tag are not accounted for in this analysis</p>
<p>Next to the size is how much the size has increased or decreased compared with the base branch of this PR. If this percentage has increased by 20% or more, there will be a red status indicator applied, indicating that special attention should be given to this.
</details>
<!-- __NEXTJS_BUNDLE_@safe-global/web -->
<!-- Sticky Pull Request Commentnext-bundle-analysis -->
author_association: CONTRIBUTOR