diff --git a/src/components/dashboard/FirstSteps/index.tsx b/src/components/dashboard/FirstSteps/index.tsx
new file mode 100644
index 0000000000..ec1473b7be
--- /dev/null
+++ b/src/components/dashboard/FirstSteps/index.tsx
@@ -0,0 +1,77 @@
+import useBalances from '@/hooks/useBalances'
+import { useAppSelector } from '@/store'
+import { selectOutgoingTransactions } from '@/store/txHistorySlice'
+import { type ReactNode } from 'react'
+import { Card, WidgetBody, WidgetContainer } from '@/components/dashboard/styled'
+import { LinearProgress, List, ListItem, ListItemIcon, Typography } from '@mui/material'
+import CircleOutlinedIcon from '@mui/icons-material/CircleOutlined'
+import CheckCircleRoundedIcon from '@mui/icons-material/CheckCircleRounded'
+
+const StatusItem = ({ children, completed = false }: { children: ReactNode; completed?: boolean }) => {
+ return (
+
+ theme.palette.primary.main }}>
+ {completed ? : }
+
+ {children}
+
+ )
+}
+
+type StatusProgressItems = Array<{
+ name: string
+ completed: boolean
+}>
+
+const StatusProgress = ({ items }: { items: StatusProgressItems }) => {
+ const totalNumberOfItems = items.length
+ const completedItems = items.filter((item) => item.completed)
+ const progress = Math.ceil(completedItems.length / totalNumberOfItems) * 100
+
+ return (
+ <>
+
+ {items.map(({ name, completed }) => {
+ return (
+
+ {name}
+
+ )
+ })}
+
+
+
+ {progress}% completed
+
+ >
+ )
+}
+
+const FirstSteps = () => {
+ const { balances } = useBalances()
+ const outgoingTransactions = useAppSelector(selectOutgoingTransactions)
+
+ const hasNonZeroBalance = balances && (balances.items.length > 1 || BigInt(balances.items[0]?.balance || 0) > 0)
+ const hasOutgoingTransactions = !!outgoingTransactions && outgoingTransactions.length > 0
+
+ const items = [
+ { name: 'Add funds', completed: hasNonZeroBalance },
+ { name: 'Create your first transaction', completed: hasOutgoingTransactions },
+ { name: 'Safe is ready', completed: hasNonZeroBalance && hasOutgoingTransactions },
+ ]
+
+ return (
+
+
+
+
+ First steps
+
+
+
+
+
+ )
+}
+
+export default FirstSteps
diff --git a/src/store/txHistorySlice.ts b/src/store/txHistorySlice.ts
index d9717d0a3b..bf7d0fb8fe 100644
--- a/src/store/txHistorySlice.ts
+++ b/src/store/txHistorySlice.ts
@@ -1,6 +1,7 @@
import type { listenerMiddlewareInstance } from '@/store'
+import { createSelector } from '@reduxjs/toolkit'
import type { TransactionListPage } from '@safe-global/safe-gateway-typescript-sdk'
-import { isTransactionListItem } from '@/utils/transaction-guards'
+import { isCreationTxInfo, isIncomingTransfer, isTransactionListItem } from '@/utils/transaction-guards'
import { txDispatch, TxEvent } from '@/services/tx/txEvents'
import { selectPendingTxs } from './pendingTxsSlice'
import { makeLoadableSlice } from './common'
@@ -10,6 +11,12 @@ const { slice, selector } = makeLoadableSlice('txHistory', undefined as Transact
export const txHistorySlice = slice
export const selectTxHistory = selector
+export const selectOutgoingTransactions = createSelector(selectTxHistory, (txHistory) => {
+ return txHistory.data?.results.filter(isTransactionListItem).filter((tx) => {
+ return !isIncomingTransfer(tx.transaction.txInfo) && !isCreationTxInfo(tx.transaction.txInfo)
+ })
+})
+
export const txHistoryListener = (listenerMiddleware: typeof listenerMiddlewareInstance) => {
listenerMiddleware.startListening({
actionCreator: txHistorySlice.actions.set,
diff --git a/src/utils/transaction-guards.ts b/src/utils/transaction-guards.ts
index 06b1591131..817dca0591 100644
--- a/src/utils/transaction-guards.ts
+++ b/src/utils/transaction-guards.ts
@@ -100,6 +100,10 @@ export const isOutgoingTransfer = (txInfo: TransactionInfo): boolean => {
return isTransferTxInfo(txInfo) && txInfo.direction.toUpperCase() === TransferDirection.OUTGOING
}
+export const isIncomingTransfer = (txInfo: TransactionInfo): boolean => {
+ return isTransferTxInfo(txInfo) && txInfo.direction.toUpperCase() === TransferDirection.INCOMING
+}
+
// TransactionListItem type guards
export const isLabelListItem = (value: TransactionListItem): value is Label => {
return value.type === TransactionListItemType.LABEL