diff --git a/packages/web/src/pages/wallet-page/WalletPage.tsx b/packages/web/src/pages/wallet-page/WalletPage.tsx
index d8d66435e0b..01b75054d3f 100644
--- a/packages/web/src/pages/wallet-page/WalletPage.tsx
+++ b/packages/web/src/pages/wallet-page/WalletPage.tsx
@@ -13,6 +13,7 @@ import { useIsMobile } from 'hooks/useIsMobile'
import { WalletCoinsList } from 'pages/wallet-page/components/WalletCoinsList'
import { LinkedWallets } from './components/LinkedWallets'
+import { SecureWalletReminderModal } from './components/SecureWalletReminderModal'
const messages = {
title: 'Wallet'
@@ -56,6 +57,7 @@ export const WalletPage = () => {
+
)
diff --git a/packages/web/src/pages/wallet-page/components/SecureWalletReminderModal.tsx b/packages/web/src/pages/wallet-page/components/SecureWalletReminderModal.tsx
new file mode 100644
index 00000000000..d94ad50b015
--- /dev/null
+++ b/packages/web/src/pages/wallet-page/components/SecureWalletReminderModal.tsx
@@ -0,0 +1,94 @@
+import { useCallback, useEffect, useState } from 'react'
+
+import { useAudioBalance } from '@audius/common/api'
+import { AUDIO } from '@audius/fixed-decimal'
+import {
+ Button,
+ Modal,
+ ModalContent,
+ ModalContentText,
+ ModalHeader,
+ ModalTitle,
+ ModalFooter,
+ IconShieldCheck
+} from '@audius/harmony'
+
+import { localStorage } from 'services/local-storage'
+
+const FAQ_URL =
+ 'https://help.audius.co/product/crypto-wallet-faq-for-audius-users'
+const DISMISS_STORAGE_KEY = 'secureWalletReminderDismissedAt'
+const DISMISS_DURATION_MS = 14 * 24 * 60 * 60 * 1000 // 2 weeks
+const BALANCE_THRESHOLD = AUDIO(1000).value
+
+const messages = {
+ title: 'Protect your $AUDIO',
+ body1:
+ "You're holding 1000 $AUDIO or more. To keep your funds safe, we recommend moving your balance to an external wallet that only you control.",
+ body2:
+ "Self-custody means your $AUDIO isn't tied to a single account: you hold the keys, so you're protected even if your password is compromised. Any external wallet can be connected to your Audius account to keep badges and access.",
+ body3: 'Read the FAQ to learn how to transfer your balance securely.',
+ dismiss: 'Dismiss',
+ readFaq: 'Read the FAQ'
+}
+
+const wasRecentlyDismissed = async () => {
+ const dismissedAt = await localStorage.getItem(DISMISS_STORAGE_KEY)
+ if (!dismissedAt) return false
+ const timestamp = Number(dismissedAt)
+ if (Number.isNaN(timestamp)) return false
+ return Date.now() - timestamp < DISMISS_DURATION_MS
+}
+
+export const SecureWalletReminderModal = () => {
+ const [isOpen, setIsOpen] = useState(false)
+ const { accountBalance, isLoading } = useAudioBalance()
+
+ useEffect(() => {
+ if (isLoading || isOpen) return
+ if (accountBalance < BALANCE_THRESHOLD) return
+
+ let cancelled = false
+ const maybeOpen = async () => {
+ const dismissed = await wasRecentlyDismissed()
+ if (!cancelled && !dismissed) {
+ setIsOpen(true)
+ }
+ }
+ maybeOpen()
+ return () => {
+ cancelled = true
+ }
+ }, [accountBalance, isLoading, isOpen])
+
+ const handleDismiss = useCallback(() => {
+ localStorage.setItem(DISMISS_STORAGE_KEY, String(Date.now()))
+ setIsOpen(false)
+ }, [])
+
+ const handleReadFaq = useCallback(() => {
+ window.open(FAQ_URL, '_blank', 'noopener,noreferrer')
+ handleDismiss()
+ }, [handleDismiss])
+
+ return (
+
+
+ } title={messages.title} />
+
+
+ {messages.body1}
+ {messages.body2}
+ {messages.body3}
+
+
+
+
+
+
+ )
+}