diff --git a/README.md b/README.md
index 27a8a6c..495f7b3 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,8 @@ React Native Expo wallet for Stellar Testnet. The app aims to feel polished and
## Documentation
+- [Navigation and Feature Boundary Map](./docs/navigation-map.md) - Canonical route tree, screen ownership, shared state, cross-feature dependencies, and high-risk contributor boundaries
+
- [Architecture Readiness Review](./docs/architecture-readiness-review.md) - Feature boundaries, duplicated state, SDK integration blockers, security-sensitive areas, and test gaps
- [Evaluation Readiness Checklist](./docs/evaluation-readiness-checklist.md) - GrantFox contributor checklist for mobile issues, including tests, CI, screenshots, acceptance criteria, and the reminder that merge does not guarantee payment approval
- [Storage Guide](./docs/storage.md) - SecureStore vs AsyncStorage
diff --git a/docs/navigation-map.md b/docs/navigation-map.md
index e33a4a2..5a9baaa 100644
--- a/docs/navigation-map.md
+++ b/docs/navigation-map.md
@@ -1,274 +1,400 @@
-# PocketPay Mobile — Navigation Map
+# PocketPay Mobile Navigation and Feature Boundary Map
-This document answers the onboarding questions:
+This is the contributor-facing map for PocketPay Mobile. Use it to determine:
-- *"Which screen file do I open to edit the Home balance view?"*
-- *"Which store slice owns wallet create/import vs. vault time-locks?"*
-- *"What happens if I add a new route group inside `app/` — do I need to update the auth gate?"*
-- *"Which screen do users land on after a successful payment?"*
+1. which route and screen own a user flow;
+2. which store, hook, or service owns its state;
+3. which shared modules it depends on;
+4. which navigation, persistence, and security boundaries require extra care.
-All file paths are relative to repository root. All routes use **Expo Router v6 file-based routing**. For routing internals see the [Expo Router documentation](https://docs.expo.dev/router/introduction/).
+The map describes the repository as it exists today. Where ownership overlaps or a boundary is incomplete, that condition is documented rather than presented as an architectural rule.
----
+Related references:
-## 1. Route Inventory (20 Routes Across 3 Groups)
+- [Screen inventory](./screen-inventory.md)
+- [Main wallet user flows](./user-flows.md)
+- [Architecture readiness review](./architecture-readiness-review.md)
+- [Storage guide](./storage.md)
+- [Mobile wallet security FAQ](./WALLET_SECURITY_FAQ.md)
+- [Vault integration assumptions](./vault-integration-assumptions.md)
+- [Release testing checklist](./release-testing-checklist.md)
-Expo Router converts files in `app/` directly to routes. Parentheses in directory names (e.g. `(auth)`) are **route groups** — they organize layout nesting but do NOT appear in the URL / deep link path.
+## 1. Navigation architecture
-### 1.1 Unauthenticated Route Group: `(auth)/`
+PocketPay uses Expo Router v6 file-based routing. `package.json` points to `expo-router/entry`.
-Shown when `walletStore.walletChecked === true` AND `publicKey === null` (no wallet on device). Stack navigator inside [app/(auth)/_layout.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/(auth)/_layout.tsx).
+| Tier | Layout | Navigator | Responsibility |
+| --- | --- | --- | --- |
+| Root shell | [`app/_layout.tsx`](../app/_layout.tsx) | `` | Startup, wallet restoration, authentication redirects, global errors, app lock, and selected-route rendering |
+| Authentication | [`app/(auth)/_layout.tsx`](../app/(auth)/_layout.tsx) | `` | Wallet creation and import |
+| Authenticated app | [`app/(tabs)/_layout.tsx`](../app/(tabs)/_layout.tsx) | `` | Home, Activity, Vault, Settings, and the shared network banner |
-| Route Path | File on Disk | Purpose | Primary Entry: Navigate Here From… |
-| ----------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -------------------------------------------- |
-| `/` (in auth) | [app/(auth)/index.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/(auth)/index.tsx) | Landing tile: **Create New Wallet** vs **Import Existing** | RootLayout auth gate / app cold start |
-| `/create` | [app/(auth)/create.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/(auth)/create.tsx) | 3-step onboarding: Generate → Reveal keys → Confirm backup | `(auth)/index` [Create New Wallet] |
-| `/import` | [app/(auth)/import.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/(auth)/import.tsx) | Paste secret → StrKey validation → activate wallet | `(auth)/index` [Import Existing] |
+### Root-layout constraint
-### 1.2 Authenticated Tab Group: `(tabs)/`
+The root layout uses ``; it does **not** declare a root ``. Root-level routes therefore manage their own header and back behavior unless a nested navigator provides it.
-Shown when `walletStore.publicKey != null` AND `walletStore.walletChecked === true`. Bottom-tab navigator inside [app/(tabs)/_layout.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/(tabs)/_layout.tsx). Adds an `OfflineBanner` at the top on all tabs.
+Do not assume that `` options inside a root-level screen configure navigation. There is no parent root stack to consume those options.
-| Route Path | File on Disk | Tab Label | Purpose |
-| ---------------- | ------------------------------------------------------------ | --------- | ------------------------------------------------------------ |
-| `/` (tabs home) | [app/(tabs)/index.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/(tabs)/index.tsx) | Home | XLM Balance, Public Key, **Fund my Wallet** button, Backup reminder modal, 5 most-recent transactions |
-| `/history` | [app/(tabs)/history.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/(tabs)/history.tsx) | Activity | **SectionList grouped by date**, filter chips (All/Sent/Received), cursor-based pagination, pull-to-refresh, empty CTA → Send |
-| `/vault` | [app/(tabs)/vault.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/(tabs)/vault.tsx) | Vault | Soroban Savings Vault: balance card, Deposit / Withdraw CTAs, Pending/Matured lock lists, intro/education modals |
-| `/settings` | [app/(tabs)/settings.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/(tabs)/settings.tsx) | Settings | 4 sections — Preferences (theme, app lock), Wallet (export, sign out), Network & Environment (new), About (version, diagnostics) |
+## 2. Startup, authentication, and global gates
-### 1.3 Stack / Modal Routes (Outside Groups)
+[`app/_layout.tsx`](../app/_layout.tsx) performs this sequence:
-Presented modally, or pushed onto the stack above the tabs.
+```text
+expo-router/entry
+ -> import shim first
+ -> install global exception and rejection handlers
+ -> initialize app preferences
+ -> restore the wallet
+ -> wait for appStore.isInitialized and walletStore.walletChecked
+ -> show secure-storage recovery UI when restoration fails
+ -> redirect between (auth) and (tabs)
+ -> wrap the selected route with ErrorBoundary and LockScreen
+ -> render through
+```
+
+The app must wait for both preference initialization and wallet restoration. Redirecting before `walletChecked` resolves can flash the onboarding flow for a returning user.
+
+### Current authentication behavior
+
+The root gate uses `router.replace`:
+
+- wallet holders inside `(auth)` are sent to `/(tabs)`;
+- users without a wallet are generally sent to `/(auth)`;
+- `send`, `receive`, and `review-transaction` are hard-coded exceptions;
+- a blocked deep link may be stored and replayed after authentication.
+
+Route names are therefore coupled to authentication behavior. Adding, renaming, or moving a route may require changing the root gate.
+
+## 3. Route inventory
+
+The grouped `(auth)/index.tsx` and `(tabs)/index.tsx` both resolve to `/`. The root gate decides which is visible.
+
+### 3.1 Authentication routes
+
+| URL | Screen | Purpose | Main dependencies |
+| --- | --- | --- | --- |
+| `/` without a wallet | [`app/(auth)/index.tsx`](../app/(auth)/index.tsx) | Choose Create or Import | Router, theme, shared controls |
+| `/create` | [`app/(auth)/create.tsx`](../app/(auth)/create.tsx) | Generate a Testnet keypair, reveal the secret, persist it, and require backup acknowledgement | `walletStore`, Stellar service, secure-storage error handling |
+| `/import` | [`app/(auth)/import.tsx`](../app/(auth)/import.tsx) | Validate and import a Stellar secret seed | `walletStore`, Stellar `StrKey`, `pocketpay-sdk` |
+| `/wallet-creation-success` | [`app/(auth)/wallet-creation-success.tsx`](../app/(auth)/wallet-creation-success.tsx) | Confirm creation and replace navigation with the tabs | Router and shared action button |
+
+New pre-wallet routes belong under `app/(auth)/` and must also be declared in [`app/(auth)/_layout.tsx`](../app/(auth)/_layout.tsx).
+
+### 3.2 Authenticated tabs
+
+| URL | Screen | Tab | Purpose | Primary state owner |
+| --- | --- | --- | --- | --- |
+| `/` with a wallet | [`app/(tabs)/index.tsx`](../app/(tabs)/index.tsx) | Home | Balance, funding, backup reminder, recent activity | `walletStore` |
+| `/history` | [`app/(tabs)/history.tsx`](../app/(tabs)/history.tsx) | Activity | Filtered and paginated transactions | `walletStore` |
+| `/vault` | [`app/(tabs)/vault.tsx`](../app/(tabs)/vault.tsx) | Vault | Balance, deposits, withdrawals, and time locks | `vaultStore` and vault feature hooks |
+| `/settings` | [`app/(tabs)/settings.tsx`](../app/(tabs)/settings.tsx) | Settings | Theme, app lock, wallet actions, contacts, network details, diagnostics | Multiple owning stores |
+
+The tab layout adds `NetworkStateBanner`. Root-level routes do not inherit it automatically.
-| Route Path | File on Disk | Presentation | Purpose | Primary Entry: Navigate Here From… |
-| --------------------- | ------------------------------------------------------------ | ------------ | ------------------------------------------------------------ | -------------------------------------------- |
-| `/send` | [app/send.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/send.tsx) | Stack push | Payment form: destination, amount, memo. Validates before navigation to sign confirmation. | Home [Send XLM], Activity [New Payment] CTA, contact row tap, scan success |
-| `/sign-confirmation` | [app/sign-confirmation.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/sign-confirmation.tsx) | Stack push | Pre-signing confirmation: shows transaction summary, explains signing implications, provides clear cancel option. Separates approval intent from execution. | Send form [Send Payment] button |
-| `/receive` | [app/receive.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/receive.tsx) | Stack push | Public-key QR code, address copy + share sheet | Home [Receive] icon / button |
-| `/scan` | [app/scan.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/scan.tsx) | Stack push | `expo-camera` QR scanner. On valid decode → navigates back to `/send` with `?destination=` prefilled. | Home [Scan] icon, Send form [Scan] button |
-| `/review-transaction` | [app/review-transaction.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/review-transaction.tsx) | Modal-like push | 8-phase Sign & Send flow: review summary → handoff → signing → submitting → completed / failed → success screen | Sign confirmation [Sign Transaction] |
-| `/payment-success` | [app/payment-success.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/payment-success.tsx) | `replace` (no back history) | Post-send confirmation: big success checkmark, tx hash copy, explorer link, Back to Home | Review transaction [phase → completed] |
-| `/contacts` | [app/contacts.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/contacts.tsx) | Stack push | Address book: list, search, add/edit/delete, duplicate guards. Tapping a contact pushes to `/send` with destination prefilled. | Settings [Address Book], Send form [Pick contact] |
-| `/diagnostics` | [app/diagnostics.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/diagnostics.tsx) | Stack push | Read-only dev/debug panel: redacted `getDiagnostics()` JSON, environment summary, synthetic-error trigger if enabled | Settings → About → [Open Diagnostics] |
-| `/transaction/:id` | [app/transaction/[id].tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/transaction/%5Bid%5D.tsx) | Stack push | **Dynamic route** (segment `:id = operation id`): single-transaction detail view, memo, fee, source/dest, explorer link | Activity row tap, Home recent-list tap |
-| `/vault/:id` | [app/vault/[id].tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/vault/%5Bid%5D.tsx) | Stack push | Dynamic: deep-linkable vault overview / account view (placeholder/detail) | Vault header card, from external deep link |
-| `/vault-lock/:id` | [app/vault-lock/[id].tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/vault-lock/%5Bid%5D.tsx) | Stack push | Dynamic: single time-lock detail — countdown, status, mature-withdraw CTA | Vault list row tap, matured notification |
+### 3.3 Root-level routes
----
+These routes are rendered through the root ``.
-## 2. Navigation Tree — ASCII Diagram
+| URL | Screen | Purpose | Navigation |
+| --- | --- | --- | --- |
+| `/send` | [`app/send.tsx`](../app/send.tsx) | Validate payment details, choose/create a contact, or scan a recipient | Pushes `/sign-confirmation` |
+| `/sign-confirmation` | [`app/sign-confirmation.tsx`](../app/sign-confirmation.tsx) | Final warning and transaction summary before execution | Pushes `/review-transaction`; cancellation replaces with tabs |
+| `/review-transaction` | [`app/review-transaction.tsx`](../app/review-transaction.tsx) | Sign, submit, track phases, retry, and report completion | Replaces with `/payment-success` |
+| `/payment-success` | [`app/payment-success.tsx`](../app/payment-success.tsx) | Show the result and explorer information | Replaces with `/(tabs)` |
+| `/receive` | [`app/receive.tsx`](../app/receive.tsx) | Display and share the receive address and QR payload | Entered from Home |
+| `/scan` | [`app/scan.tsx`](../app/scan.tsx) | Full-screen QR scanner with manual-entry fallback | Replaces with `/send?destination=...` |
+| `/contacts` | [`app/contacts.tsx`](../app/contacts.tsx) | Add, scan, update, and remove contacts | Entered from Settings |
+| `/diagnostics` | [`app/diagnostics.tsx`](../app/diagnostics.tsx) | Show redacted diagnostics and environment information | Entered from Settings |
+| `/transaction/:id` | [`app/transaction/[id].tsx`](../app/transaction/[id].tsx) | Render or fetch one transaction | Home, Activity, or deep link |
+| `/vault/:id` | [`app/vault/[id].tsx`](../app/vault/[id].tsx) | Vault detail/placeholder route | Vault or deep link |
+| `/vault-lock/:id` | [`app/vault-lock/[id].tsx`](../app/vault-lock/[id].tsx) | Time-lock detail and mature-withdraw state | Vault list or deep link |
-Read top-down from the single JS entry point `expo-router/entry`. Arrow `→` means "navigates to"; `—` means "wraps as child"; `↩` means "navigates back with result / prefilled params".
+### Route cautions
+- `/scan` and the scanner embedded in `/send` are separate implementations.
+- The root scanner sets a `destination` query parameter; verify that Send consumes it before relying on that handoff.
+- Root-level screens use multiple header conventions.
+- There is no `app/+not-found.tsx`; unmatched deep links have no app-specific route.
+
+## 4. Primary flows
+
+### Wallet creation
+
+```text
+(auth) landing
+ -> /create
+ -> generate keypair
+ -> reveal and confirm backup
+ -> walletStore.setWallet() stores the secret in SecureStore
+ -> walletStore.markBackupPending()
+ -> /wallet-creation-success
+ -> replace with /(tabs)
```
- package.json: "main": "expo-router/entry"
- │
- ▼
- app/_layout.tsx (Root Stack)
- ├─ preload: shim.js (polyfills FIRST)
- ├─ ErrorBoundary + LockScreen wrap
- └─ Auth redirect gate (walletChecked + publicKey)
- │
- ┌─ NO wallet ───────┴───────── YES wallet ──┐
- │ │
- ▼ ▼
- (auth)/_layout.tsx (tabs)/_layout.tsx
- ┌────────────────────┐ ┌─── OFFLINE BANNER ────┐
- │ index (Create/Imp) │ │ Home Activity │
- │ ▼ ▼ │ │ Vault Settings │
- │ create import │ └────────────────────────┘
- └────────────────────┘ │
- ┌────────────┴─────────────┬──────────────┬────────────┬───────────────┐
- ▼ ▼ ▼ ▼ ▼
- /send /receive /scan /contacts /diagnostics
- │ (form validates) │
- ▼ └── on decode: /send ?destination=...
- /sign-confirmation
- │ (user approves signing intent)
- ▼
- /review-transaction
- │
- (8-phase signer SM)
- │ success: replace (no back)
- ▼
- /payment-success → "Back to Home" → /(tabs)
-
- DYNAMIC DEEP-LINKABLE ROUTES (stack-pushed from within their parent screen)
- ├─ /transaction/:id ← tap row in Home recent / Activity list
- ├─ /vault/:id ← tap Vault header / deep link scheme://stellar-pocketpay/vault/123
- └─ /vault-lock/:id ← tap lock row in Vault list
+
+### Wallet import
+
+```text
+(auth) landing
+ -> /import
+ -> validate secret seed
+ -> pocketpay-sdk importWallet()
+ -> walletStore.setWallet()
+ -> success state
+ -> replace with /(tabs)
```
-### 2.1 Back-Stack Behavior — Key Rules
+### Send and signing
+
+```text
+Home or payment entry
+ -> /send
+ -> validate address, amount, memo, balance, funding, and network state
+ -> /sign-confirmation
+ -> explicit user approval
+ -> /review-transaction
+ -> signerStore: review -> handoff -> signing -> submitting -> confirming
+ -> completed or failed
+ -> optimistic transaction added to walletStore
+ -> replace with /payment-success
+ -> replace with /(tabs)
+```
-Document these behaviors explicitly when modifying navigation:
+Using `replace` after completion is intentional: it prevents back-navigation into a stale signing flow.
-- `/payment-success` uses **`router.replace`**, not `push`. The user cannot go "Back to signing" → always jumps to Home. This prevents accidental duplicate sends. See: [app/review-transaction.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/review-transaction.tsx)
-- RootLayout auth gate uses **`router.replace`** when swapping `(auth)`↔`(tabs)`. This means tapping hardware Back after sign-out does NOT pop a tab screen back into view. See: [app/_layout.tsx auth gate](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/_layout.tsx#L46-L66)
-- `/scan` calls **`router.back()` after setting params** on a successful decode. This returns to the form, preserving what the user had already typed into Amount/Memo before scanning. See [app/scan.tsx](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/app/scan.tsx).
-- Dynamic routes `/transaction/:id`, `/vault/:id`, `/vault-lock/:id` accept **both deep links and programmatic pushes**. They must render gracefully even when the parent store state is empty (e.g. user taps a stored deep link before the wallet loads — show a loading or "Wallet not loaded yet" placeholder).
+### Receive and scanning
-### 2.2 Deep Links (Scheme: `stellar-pocketpay://`)
+```text
+Home -> /receive -> render/copy/share receive payload
+Home -> /scan -> QR result -> replace with /send?destination=...
+/send -> embedded QrScanner -> write address directly into form state
+```
-Configured in `app.json` → `scheme: "stellar-pocketpay"`. Currently 5 deep-link targets are supported because Expo Router maps file paths 1:1 to scheme paths:
+### Activity detail
-| Deep link | Resolves to | Prerequisite |
-| ----------------------------------- | ---------------------------------- | ------------------------------------- |
-| `stellar-pocketpay:///` | Home (if wallet) or auth (if not) | — |
-| `stellar-pocketpay:///send?destination=G…` | Prefilled Send form | `destination` must be valid strkey |
-| `stellar-pocketpay:///receive` | Receive screen | wallet loaded |
-| `stellar-pocketpay:///transaction/tx-123` | Transaction detail | wallet loaded **or** network fetch succeeds; tx ID validated |
-| `stellar-pocketpay:///vault-lock/4` | Single lock detail view | wallet loaded + id 4 in vaultStore.locks |
+```text
+Home recent row or Activity row
+ -> /transaction/:id
+ -> use walletStore data when present
+ -> fetch from Horizon when absent
+ -> render loading, detail, not-found, or retry state
+```
+
+### Vault
+
+```text
+Vault tab
+ -> load availability, balance, and locks
+ -> deposit/withdraw/lock orchestration
+ -> /vault/:id
+ -> /vault-lock/:id
+```
+
+The vault may be mock-backed or use a configured Soroban integration. A UI success state must not be documented as confirmed settlement without checking the active configuration and confirmation path.
-#### Transaction Deep Link Behavior
+## 5. Feature ownership
-The transaction detail route (`/transaction/[id]`) supports full deep link resolution:
+This table describes current ownership, not an ideal future folder structure.
-1. **In-memory lookup first** — if the transaction is already in the wallet store (e.g. user just sent it), it renders instantly.
-2. **Network fetch fallback** — if not in the store, the app fetches the operation from Horizon via `fetchOperationById()`. Shows a loading spinner during fetch.
-3. **Validation** — the `id` param is validated (non-empty, reasonable length, no control characters) before any network request. Invalid IDs show a clear error state.
-4. **Not found / error states** — if the transaction doesn't exist on the network or the fetch fails, the user sees a descriptive error with Retry and Go Back actions.
-5. **Auth gating** — if a logged-out user arrives via deep link, the URL is preserved and replayed after authentication completes (via `pendingDeepLink` in `app/_layout.tsx`).
+| Feature | Screen ownership | State/orchestration | Services and deep dependencies |
+| --- | --- | --- | --- |
+| Wallet lifecycle | Create, Import, wallet section in Settings | `src/store/walletStore.ts` | `src/services/stellar.ts`, `pocketpay-sdk`, SecureStore, AsyncStorage backup acknowledgement |
+| Balance and funding | Home, Send warnings, Activity | `walletStore` | Horizon and Friendbot helpers |
+| Transactions | Home preview, Activity, transaction detail | `walletStore`, transaction feature/util modules | Horizon access in Stellar service |
+| Send and signing | Send, Sign Confirmation, Review Transaction, Payment Success | `walletStore`, `signerStore`, and screen orchestration | Stellar service, wallet secret, contact labels |
+| Receive and QR | Receive, Scan, Send/Contacts scanner surfaces | Mostly local screen/component state | Expo Camera, `QrScanner`, QR rendering library |
+| Contacts | Contacts screen and Send contact UI | `appStore` for Contacts; `contactStore` for parts of Send | AsyncStorage, validation and normalization |
+| Vault | Vault tab and dynamic vault routes | `vaultStore`, `src/features/vault/`, vault hooks | Vault/Stellar services, wallet secret, Soroban configuration |
+| Settings/theme | Settings | `appStore` plus feature-specific stores | AsyncStorage, environment configuration |
+| App lock/security | Root LockScreen and Settings | `appLockStore`, `walletStore` | Local Authentication, SecureStore, reset UI |
+| Diagnostics/errors | Root recovery UI and Diagnostics | ErrorBoundary, global error handler, reporting utilities | Redaction and environment metadata |
----
+## 6. Store and persistence boundaries
-## 3. Feature Boundaries & Ownership Grid
+| Store | Owns | Cross-feature consumers |
+| --- | --- | --- |
+| `walletStore` | Public key, secret access, balance, funding, transactions, wallet reset, backup reminder | Root, auth, Home, Activity, Send, signing, Receive, Vault, Settings |
+| `appStore` | Theme, app initialization, one contacts collection | Root, theme, Contacts, Send labels, Settings |
+| `signerStore` | Transaction review/signing phase machine | Review Transaction and payment completion |
+| `vaultStore` | Vault config, balance, locks, action state | Vault routes and Settings reset paths |
+| `appLockStore` | Lock preference and biometric authentication | Root LockScreen and Settings |
+| `contactStore` | A second contacts collection and recent recipients | Send feature |
-This is the most important section for contributors. If you are working on *feature X*, the "OWNED BY" columns tell you which files to edit and which you should touch only via exported actions/hooks.
+### Persistence map
-**Legend:**
+| Data | Storage | Owner | Caution |
+| --- | --- | --- | --- |
+| Wallet secret | SecureStore | `walletStore` | Never move into AsyncStorage, route params, logs, diagnostics, screenshots, or persisted Zustand state |
+| Wallet runtime data | Memory and network refresh | `walletStore` | A public key alone does not prove secret restoration succeeded |
+| Backup acknowledgement | AsyncStorage | `walletStore` | Must survive restarts until acknowledged |
+| Theme and `appStore` contacts | AsyncStorage | `appStore` | Parse stored values defensively |
+| Feature contacts and recent recipients | Persisted Zustand/AsyncStorage | `contactStore` | Separate from `appStore.contacts` |
+| Vault state | Store/service-specific persistence | `vaultStore` and vault utilities | Treat local financial-looking state as cache/placeholder unless contract-confirmed |
-- 🟢 **Primary — edit here first.** The file owns the logic/UI for this feature.
-- 🟡 **Secondary — read-only / call via exported hooks/actions.** Depend on this, do not modify unless the feature needs a new capability.
-- 🔗 **Deep dependency — rarely need to modify.** Core layout/shell, shared types, shared components.
-- 🚫 **Never modify for this feature.** Would break invariants.
+### Duplicate contacts boundary
-### 3.1 Ownership Grid (7 Feature Areas)
+There are currently two contacts sources:
-| Feature Area | UI Entry: File(s) 🟢 | Hooks / Feature Logic 🟢 | Store Owner 🟢 | Service Layer 🟡 | Shared Components 🟡 |
-| ------------------------ | ----------------------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -------------------- |
-| **1. Wallet Lifecycle** | `(auth)/create.tsx` `(auth)/import.tsx` `(tabs)/settings.tsx Wallet section` | — (inline orchestration in screen; import logic helper `parseStoredSecret` in store) | [store/walletStore.ts](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/src/store/walletStore.ts) — create/import/export/clear, backup reminders | [services/stellar.ts](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/src/services/stellar.ts#L17-L24) — `generateKeypair`, `validateSecretKey`; also SecureStore via store | SecretKeyReveal, WalletResetConfirmModal, BackupReminderModal |
-| **2. Balance / History** | `(tabs)/index.tsx` (Top 5 + Balance card) `(tabs)/history.tsx` `transaction/[id].tsx` | Pagination inside walletStore actions; pull-to-refresh inline | walletStore → balance, transactions[], nextCursor, isLoadingMore | stellar.ts → fetchXlmBalance, fetchTransactionsPage, getExplorerTxUrl | TransactionListItem, EmptyState, NetworkStatusBanner |
-| **3. Send / Receive** | `send.tsx` `receive.tsx` `review-transaction.tsx` `payment-success.tsx` `scan.tsx` | **[hooks/useSignerHandoff.ts](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/src/hooks/useSignerHandoff.ts)** — 8-phase orchestrator; validation.ts pure funcs | walletStore (getSecretKey, refresh after send), **[store/signerStore.ts](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/src/store/signerStore.ts)** — handoff phases, lastResult | stellar.ts → sendXlmTransaction, fetchBaseFee; signer.ts → LocalSigner; pocketpay-sdk → validatePublicKey | QrScanner, FundButton, SigningConfirmModal, PaymentErrorBanner, DirtyFormConfirm |
-| **4. QR / Scanner** | `scan.tsx` `receive.tsx` (QR render) | — (inline Expo Camera barcode callbacks) | walletStore publicKey read only | — | **QrScanner** (component) + react-native-qrcode-svg lib |
-| **5. Address Book** | `contacts.tsx` + Send form contact picker flow | dedup helpers in `utils/contacts.ts` | [store/appStore.ts](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/src/store/appStore.ts) → contacts[] + addContactIfUnique + removeContact | — (persists via AsyncStorage inside appStore actions) | FormField, ConfirmModal |
-| **6. Vault** | `(tabs)/vault.tsx` `vault/[id].tsx` `vault-lock/[id].tsx` | **[features/vault/](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/src/features/vault)** — maturedLockWithdrawal, useMaturedLockWithdrawal, useVaultDepositForm, useVault | **[store/vaultStore.ts](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/src/store/vaultStore.ts)** — balance, locks[], isConfigured | **[services/vault.ts](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/src/services/vault.ts)** — Soroban RPC calls; stellar.ts — mock fallbacks | All Vault* Modal & List components (MaturedLockWithdrawalModal, VaultIntroModal, LockDurationSelector, …) |
-| **7. Settings / Network / Theme / AppLock** | `(tabs)/settings.tsx` `diagnostics.tsx` | **NEW:** [features/settings/useNetworkEnvironment.ts](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/src/features/settings/useNetworkEnvironment.ts) (Network tier, hostname mask, warnings); [hooks/useTheme.ts](file:///c:/Users/Muhammad/.trae/Grantfox/pocketpay-mobile/src/hooks/useTheme.ts) | appStore (theme, init), walletStore (secret export, clear), appLockStore (lock flag, biometrics authenticateAsync), vaultStore (locks purge, intro reset) | stellar.ts/vault.ts read only (isVaultConfigured); AsyncStorage + SecureStore via stores | AsyncActionButton, ConfirmModal, LockScreen |
+1. `appStore.contacts`, used by the main Contacts screen and payment label resolution;
+2. `contactStore.contacts`, used by Send for embedded contact creation and recent recipients.
-### 3.2 Cross-Feature Boundary Rules (Hard Invariants)
+Updating one does not update the other. Do not introduce a third source. Any contact-related change must identify which store is authoritative for that flow and account for migration or consolidation when crossing the boundary.
-1. **No screen imports a `services/` module directly.** Always go through the owning store/hook. The single exception today is `getExplorerTxUrl()` (pure string formatter — may be relaxed).
-2. **No store imports another store.** If store A needs store B's data, read it inline inside the screen/hook with two separate `useXxxStore(…)` calls and combine locally.
-3. **`features/` modules never import from `app/` (screens).** Features are pure logic + hooks; dependency arrow must point from app→features, never reverse.
-4. **`components/` never import a store.** Always pass props/callbacks from the screen. This keeps the 36-component library portable.
+## 7. Shared components
----
+Shared UI lives under `src/components/`.
-## 4. End-to-End Payment Flow — Navigation Path (Send → Review → Success)
+| Category | Examples | Boundary |
+| --- | --- | --- |
+| Navigation/chrome | `ScreenHeader`, `NetworkStateBanner`, `OfflineBanner` | Route decisions remain in layouts/screens |
+| Actions/forms | `Button`, `AsyncActionButton`, `FormField`, `Input`, `ConfirmModal`, `DirtyFormConfirm` | Service calls and validation remain with the owner |
+| States/status | `LoadingState`, `EmptyState`, `WalletEmptyState`, `StatusBadge`, `FundingStatusBanner` | Preserve consistent loading, error, and accessibility behavior |
+| Security/recovery | `LockScreen`, `SecretKeyReveal`, `WalletResetConfirmModal`, `BackupReminderModal` | Do not weaken authentication, masking, or destructive confirmation |
+| Payments/contacts | `ReviewConfirm`, `QrScanner`, `ContactPicker`, `ContactForm`, `PaymentErrorBanner` | Keep signing and persistence outside presentation components |
+| Vault | Vault modals, lists, details, and unavailable state | Shared UI must not decide whether data is mock or contract-confirmed |
-Contributor onboarding reference: every step along the send/receive user journey with navigation events marked.
+Preferred dependency direction:
+```text
+screen/layout -> feature hook or store action -> service/SDK
+screen/layout -> shared component through props
```
- USER ACTIONS FILES / NAV EVENTS
- ───────────────── ─────────────────────────────────────────────────────────────────
- ▼ Taps [Send XLM] Home (index.tsx) → router.push('/send')
- on Home tile
- │
- ▼ Enters: dest + amount + /send.tsx
- memo. [Scan] also ok → ├─ Validates address, amount, memo (inline)
- prefills dest └─ All pass? → router.push('/review-transaction?dest=...&amt=...&memo=...')
- │
- ▼ Review screen renders: /review-transaction.tsx
- amount + fee + dest + memo ├─ signerStore.startReview() → phase = 'review'
- "Sign & Send" CTA ├─ USER TAPS [Sign & Send]
- │ │ ├─ useSignerHandoff.initiateSigning()
- │ │ └─ phases: handoff → signing → submitting → completed
- │ │ └─ service call: stellar.sendXlmTransaction
- │ │
- │ └─ phase==='completed' → setTimeout 1500ms →
- │ router.replace('/payment-success?hash=...')
- ▼ "Payment successful" + /payment-success.tsx
- hash + explorer link └─ [Back to Home] → router.replace('/(tabs)')
- │
- User is back on Home. └─ walletStore.refreshWalletData(publicKey) has fired automatically
- → new balance visible, top of Activity shows the new row
+
+This is a target boundary, not a fully enforced rule; some screens currently import services directly.
+
+## 8. Cross-feature dependencies
+
+```text
+Root shell
+ -> appStore initialization
+ -> walletStore restoration
+ -> appLockStore through LockScreen
+ -> ErrorBoundary and globalErrorHandler
+
+Wallet/transactions
+ -> walletStore
+ -> services/stellar
+ -> SecureStore and Horizon/Friendbot
+
+Payment
+ -> walletStore
+ -> signerStore
+ -> appStore contacts for labels
+ -> contactStore for Send contacts/recent recipients
+ -> services/stellar
+
+Vault
+ -> walletStore identity/secret access
+ -> vaultStore
+ -> vault feature hooks
+ -> vault service or mock fallback
+ -> Soroban configuration
+
+Settings
+ -> appStore
+ -> appLockStore
+ -> walletStore
+ -> vaultStore
+ -> Contacts and Diagnostics routes
```
-**Why `replace` instead of `push` here?** If `payment-success` used `push`, the user could tap "Back" on the OS and land on Review Transaction *again*, with a stale amount already pre-signed and the Sign button still clickable — an invite for double-spend UX bugs. `replace` cleans the stack.
+Changes to `walletStore`, `services/stellar.ts`, root auth logic, or storage utilities require regression checks across several features.
----
+## 9. Global error and recovery boundaries
-## 5. Route Assumptions & Adding New Routes
+| Layer | Owner | Handles |
+| --- | --- | --- |
+| Render boundary | `ErrorBoundary` | React render/lifecycle errors |
+| Global JS handler | `globalErrorHandler.ts` | Exceptions outside render boundaries |
+| Rejection tracking | `globalErrorHandler.ts` | Unhandled promises |
+| Reporting/redaction | Error reporting and diagnostics utilities | Safe metadata without secrets |
+| Wallet recovery | Root layout | SecureStore failure, retry, destructive reset |
+| Network state | Tab layout and selected root screens | Offline/degraded state and write disabling |
+| Feature recovery | Owning screen/store/hook | Validation, submission, vault, contact, transaction errors |
-When you add a new route under `app/`, these assumptions are baked into the current architecture — **you are opting in to all of them** unless you explicitly modify the root layout or create a new route group.
+Global handlers report and then preserve the platform’s default crash/RedBox behavior. They must not silently swallow fatal errors.
-### 5.1 Assumption A — Every route gets the 3 safety wrappers automatically
+## 10. High-risk caution zones
-Every file under `app/` (including new ones you add) inherits, top-down:
+### Secret access
-1. **Polyfills loaded FIRST** via `shim.js` at `app/_layout.tsx:1` (Buffer, process, crypto.getRandomValues). If you add a new screen that imports StellarSdk before root renders (don't), it will throw.
-2. **`ErrorBoundary` → `ErrorBoundaryFallback`** on render exceptions. (No partial crashes.)
-3. **`GestureHandlerRootView`** → if your screen uses swipeable rows, gesture handlers work without re-wrapping.
-4. **App Lock gate (`LockScreen` wrapper around ``)** — if `appLockStore.isLockEnabled && !isAuthenticated`, the screen is hidden behind biometrics regardless of how the user arrived (deep link, OS relaunch, or push notification).
-5. **`NetworkStatusBanner` / `OfflineBanner`** — in `(tabs)/` sub-layout, banners render. Outside `(tabs)/`, you may need to include it yourself if offline behavior matters for the new screen.
+- `walletStore.getSecretKey()` crosses the app’s most sensitive boundary.
+- Never pass secrets through routes, AsyncStorage, logs, analytics, diagnostics, screenshots, or user-visible errors.
+- Preserve confirmation and clear failure states for export/reset operations.
-### 5.2 Assumption B — Auth redirection gate runs for EVERY route, not just group roots
+### Polyfill order
-The `useEffect` in `app/_layout.tsx` (lines 46–66) redirects to `/(auth)` or `/(tabs)` based on:
+The root imports `shim` first for Stellar and cryptographic dependencies. Do not move it below other imports or import the Stellar SDK from an earlier startup module.
-- `appStore.isInitialized === true` AND
-- `walletStore.walletChecked === true`
+### Auth and deep links
-**If you add a new route in a new group, e.g. `(payments)/something.tsx`**, the root layout still knows nothing about it and will redirect based on the same two rules. If your new group is **meant to be reachable without a wallet** (e.g. a marketing onboarding flow), move it inside `(auth)/` — the gate redirects *any* wallet-absent route into the `(auth)` group.
+- Route names are coupled to the root allowlist.
+- Deep links can arrive before initialization completes.
+- Validate dynamic parameters before network access.
+- `push`, `replace`, and `back` are security and duplicate-action concerns, not cosmetic choices.
-### 5.3 Assumption C — Deep links always work (file path = URL)
+### Payments
-Because we use `scheme: "stellar-pocketpay"` and Expo Router's filesystem-routing 1:1 mapping, any new file `/app/foo/bar.tsx` will instantly be reachable via `stellar-pocketpay:///foo/bar`.
+- Scanning or choosing a contact must never sign automatically.
+- Signing requires explicit confirmation.
+- Client timeout does not prove network rejection.
+- Success must remove stale signing screens from back navigation.
-**Secure your new routes the same way the existing ones are.** Example safe patterns:
+### Contacts
-- Dynamic routes `/transaction/:id` and `/vault-lock/:id` should check `publicKey` on mount and redirect to `/(auth)` when missing (never render a partial screen with empty on-chain data).
-- If the parameter references a non-existent ID (user hand-edited the scheme URL), show a user-friendly "Transaction not found" state inside the dynamic screen — not a crash.
+- Two persisted stores overlap.
+- Normalize addresses and preserve duplicate checks.
+- Contact names are labels; the Stellar address remains authoritative.
-### 5.4 Assumption D — The `(tabs)` root and `(auth)` root both mount at `/`
+### Vault
-Two files are both named `index.tsx` inside different route groups: `(auth)/index.tsx` and `(tabs)/index.tsx`. **Only one renders at a time**, decided by the root redirect gate.
+- Confirm mock versus contract-backed mode.
+- Do not infer finality from submission alone.
+- Lock IDs, maturity calculations, and local persistence require tests.
-This means:
+### Diagnostics
-- ❌ Do NOT add a top-level `app/index.tsx` alongside the two group index files — it will win the routing ambiguity and break the gate.
-- ✅ When you want to add a *new unauthenticated screen*, always add it inside `(auth)/` as a sibling of `create.tsx`.
-- ✅ When you want to add a *new authenticated tab* (e.g. `explore`), add it inside `(tabs)/` AND add its tab entry in `(tabs)/_layout.tsx` tabs config.
+- Keep diagnostics read-only and redacted.
+- Never expose secret seeds, authorization material, or raw storage.
+- Keep synthetic errors separate from normal user flows.
----
+## 11. Contributor checklist
-## 6. Cross-Reference — Other Docs Related to Navigation & Features
+For a route change:
-This document is one of several contributor references in `docs/`.
+1. Update the route file and grouped navigator when applicable.
+2. Check root auth and deep-link replay.
+3. Decide whether the route needs network-state UI.
+4. Preserve intentional `push`, `replace`, or `back`.
+5. Validate route parameters.
+6. Identify the owning store and avoid duplicate state.
+7. Preserve secret and persistence boundaries.
+8. Reuse shared components and UI-state patterns.
+9. Update tests and the release checklist.
+10. Update this map when ownership or navigation changes.
-| Document | What It Covers / Cross-Reference |
-| ------------------------------------------------------------ | --------------------------------- |
-| [screen-inventory.md](./screen-inventory.md) | Per-screen UI state catalog (loading / empty / error / …). Complementary to this nav map: use it after you've found the right file. |
-| [user-flows.md](./user-flows.md) | 9 detailed diagrams of user journeys, including onboarding, send, vault lock maturity. Read this before editing a flow you haven't touched. |
-| [signer-handoff-design.md](./signer-handoff-design.md) | The 8-phase signer state machine's original design rationale, external signer/hardware wallet roadmap. |
-| [vault-integration-assumptions.md](./vault-integration-assumptions.md) | Contract addresses, test accounts, mock-vs-real switch. Required context before modifying `/vault`, `/vault/:id`, or `/vault-lock/:id`. |
-| [release-testing-checklist.md](./release-testing-checklist.md) | Manual QA that re-traces every route above. If you change a route or add a new one, **you MUST add corresponding boxes to this checklist.** |
-| [accessibility.md](./accessibility.md) | A11y labels, touch targets, focus order, announcements — apply to *every* new route you add. |
-| [contacts.md](./contacts.md) | Address book duplicate rules, data model, expected edge cases — specific to `/contacts`. |
-| [qr-payment-requests.md](./qr-payment-requests.md) | QR format / BIP-21 style prefix plans, future extensions — specific to `/receive` + `/scan`. |
+For a shared store/service change:
----
+1. Search all consumers.
+2. Check cold start and restoration.
+3. Test empty, loading, error, retry, and offline states.
+4. Check direct navigation and deep links.
+5. Verify persistence and malformed stored data.
+6. Confirm logs and diagnostics remain redacted.
-## 7. Quick Finder — "I Need To Change … Where?"
+## 12. Quick ownership finder
-Contributor cheat sheet. If you're about to make one of these common changes, open the left file first.
+| Change | Start here | Also inspect |
+| --- | --- | --- |
+| Add pre-wallet screen | `app/(auth)/`, auth layout | Root redirect/startup |
+| Add bottom tab | `app/(tabs)/`, tab layout | Network banner and auth redirect |
+| Add root route | New `app/` file | Root ``, header/back, auth allowlist, deep links |
+| Change wallet creation/import | Auth screens, `walletStore` | SecureStore, backup and recovery |
+| Change balance/Activity | `walletStore`, Home, History | Horizon, pagination, pending transactions |
+| Change Send/signing | Send, Sign Confirmation, Review, `signerStore` | Secret access, contacts, network, success replacement |
+| Change Receive/QR | Receive, Scan, `QrScanner` | QR docs, permission states, query handling |
+| Change contacts | Contacts, `appStore`, `contactStore` | Normalization and Send picker |
+| Change vault | Vault screens, `vaultStore`, vault feature | Service, mock/live config, wallet secret |
+| Change Settings | Settings and owning stores | Reset, app lock, theme, diagnostics |
+| Change global errors | Root, ErrorBoundary, global handler | Redaction and platform handler |
-| Change Goal | Open This First 🎯 | Also Check |
-| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
-| Add a new bottom-tab screen | `(tabs)/_layout.tsx` → add Tabs.Screen entry, then create the file | root auth gate in `_layout.tsx` if it needs special auth rules |
-| Add a new pre-auth onboarding step | Add under `(auth)/` as a sibling of `create.tsx`, register in `(auth)/_layout.tsx` stack | release-testing-checklist §2 wallet flows |
-| Change the "what screen after sign-in / after sign-out" rule | Auth gate in `app/_layout.tsx` lines 46–66 | LockScreen component for app-lock behavior |
-| Rename a route file | 1) Rename the file; 2) Update every `router.push('/old-path')` site-wide (grep for the string); 3) Add a redirect or note the deep-link breakage | deep-link table §2.2 (changing file names breaks scheme URLs) |
-| Add a new Store slice (e.g. `nftStore`) | Create under `src/store/nftStore.ts`, follow slice pattern (no cross-store imports), initialize in its own module top scope. Never add to a single root combine store. | `shim.js` if new service/SDK requires an extra polyfill |
-| Add a new feature section in Settings | `(tabs)/settings.tsx` as a new `` section + new sub-constants in `features/settings/useNetworkEnvironment.ts` if env-derived | release-testing-checklist §7 Settings/Persistence |
-| Add a new payment / signing path (e.g. MUXED account, XLM path with base64 memo) | `/send.tsx` validators + `review-transaction.tsx` + `stellar.ts` send helper; route navigation stays the same | signer-handoff-design.md, user-flows.md § send payment flow |
+Keep this document synchronized with the route tree. It maps current ownership and risk; it does not replace reading the code being changed.