diff --git a/.env.example b/.env.example
index cdd779a..1f2b5ba 100644
--- a/.env.example
+++ b/.env.example
@@ -24,9 +24,19 @@ EXPO_PUBLIC_APP_ENV=development
# Get a project ID at https://cloud.walletconnect.com
# EXPO_PUBLIC_WALLET_CONNECT_PROJECT_ID=
-# Embedded wallets (Privy, optional)
-# Enable Email and Google login plus Ethereum embedded wallets in the Privy
-# dashboard. Both public values are safe to ship; never put a Privy secret here.
+# ─── Embedded Wallets — Privy (optional) ──────────────────────────────
+# Enables social/email login with automatic wallet creation for non-crypto
+# native users. When both values are set, the onboarding screen shows a
+# "Get started without a wallet" option backed by Privy's MPC embedded wallets.
+#
+# Setup:
+# 1. Create a Privy app at https://dashboard.privy.io
+# 2. Enable "Email" and "Google" login methods
+# 3. Enable "Embedded Wallets → Ethereum" with createOnLogin = users-without-wallets
+# 4. Copy the App ID and Client ID below
+#
+# Both values are public (safe to ship in the app binary). Never put a Privy
+# secret key here. See docs/embedded-wallet-provider.md for the full evaluation.
# EXPO_PUBLIC_PRIVY_APP_ID=
# EXPO_PUBLIC_PRIVY_CLIENT_ID=
diff --git a/.maestro/08-embedded-wallet-entry.yaml b/.maestro/08-embedded-wallet-entry.yaml
index 9bdb9eb..f7f0069 100644
--- a/.maestro/08-embedded-wallet-entry.yaml
+++ b/.maestro/08-embedded-wallet-entry.yaml
@@ -1,19 +1,67 @@
appId: xyz.guildpass.mobile
---
-# E2E Test: Embedded wallet entry point
+# E2E Test: Embedded wallet onboarding flow
+#
# Run this flow in a build configured with EXPO_PUBLIC_PRIVY_APP_ID and
# EXPO_PUBLIC_PRIVY_CLIENT_ID. OTP completion is intentionally not automated:
# it requires a real email inbox and verifies the provider-hosted flow.
+#
+# CI note: For automated CI runs, use a Privy test-mode app ID that returns
+# deterministic OTP codes, or mock the Privy SDK at the provider level.
- launchApp:
clearState: true
+
+# ── Onboarding screen renders ─────────────────────────────────────────
- assertVisible:
id: "onboarding-screen"
- assertVisible:
id: "onboarding-embedded-wallet-button"
+
+# ── Tap into embedded wallet flow ──────────────────────────────────────
- tapOn:
id: "onboarding-embedded-wallet-button"
- assertVisible:
id: "embedded-wallet-onboarding"
+
+# ── Custody disclosure is visible before sign-in ───────────────────────
+- assertVisible:
+ id: "custody-disclosure"
+- assertVisible:
+ text: "About your embedded wallet"
+
+# ── Expand disclosure and verify detail content ────────────────────────
+- tapOn:
+ id: "custody-disclosure-toggle"
+- assertVisible:
+ id: "custody-disclosure-details"
+- assertVisible:
+ text: "How your keys are secured"
+- assertVisible:
+ text: "Trade-offs vs. self-custody"
+- assertVisible:
+ id: "custody-disclosure-learn-more"
+
+# ── Collapse disclosure ────────────────────────────────────────────────
+- tapOn:
+ id: "custody-disclosure-toggle"
+- assertNotVisible:
+ id: "custody-disclosure-details"
+
+# ── Sign-in UI elements are present ───────────────────────────────────
+- assertVisible:
+ id: "social-google-button"
- assertVisible:
id: "embedded-wallet-email-input"
+- assertVisible:
+ id: "embedded-wallet-email-button"
+
+# ── "I have a wallet" back button returns to onboarding choice ─────────
+- assertVisible:
+ id: "embedded-wallet-back-button"
+- tapOn:
+ id: "embedded-wallet-back-button"
+- assertVisible:
+ id: "onboarding-embedded-wallet-button"
+- assertVisible:
+ id: "onboarding-get-started-button"
diff --git a/.maestro/15-embedded-wallet-disconnect.yaml b/.maestro/15-embedded-wallet-disconnect.yaml
new file mode 100644
index 0000000..aaf08bf
--- /dev/null
+++ b/.maestro/15-embedded-wallet-disconnect.yaml
@@ -0,0 +1,32 @@
+appId: xyz.guildpass.mobile
+---
+# E2E Test: Embedded wallet disconnect flow
+#
+# Verifies that an embedded-wallet connection displays the correct connection
+# label on the profile screen and that disconnect returns the user to onboarding.
+#
+# Prerequisites: This flow assumes the app is already connected with an embedded
+# wallet. In CI, seed the wallet store directly via Maestro's runScript or use a
+# Privy test-mode build that auto-completes login.
+#
+# NOTE: Full Privy SDK logout verification cannot be done in E2E without mocking
+# the provider. This test verifies the UI contract: disconnect clears the wallet
+# state and navigates back to onboarding.
+
+# ── Verify embedded wallet is shown on profile ─────────────────────────
+- assertVisible:
+ id: "profile-screen"
+- assertVisible:
+ id: "profile-connected"
+- assertVisible:
+ text: "Embedded Wallet"
+- assertVisible:
+ id: "connected-wallet-address"
+
+# ── Disconnect ─────────────────────────────────────────────────────────
+- tapOn:
+ id: "wallet-disconnect-button"
+
+# ── Verify return to wallet connect form ───────────────────────────────
+- assertVisible:
+ id: "wallet-connect-form"
diff --git a/README.md b/README.md
index 6f7fbe1..f3b8094 100644
--- a/README.md
+++ b/README.md
@@ -87,6 +87,7 @@ The application is built on a robust, feature-driven foundation designed for lon
| `app/` | Expo Router file-based pages and layouts |
| `app/access-scanner.tsx` | QR code scanner screen using expo-camera |
| `src/features/access/` | Access check hooks and QR payload validation |
+| `src/features/wallet/` | Wallet connectors, embedded wallet (Privy), stores |
| `src/` | Feature modules, hooks, stores, and services |
| `docs/` | Architecture and integration guides |
| `tests/` | Vitest unit tests |
@@ -252,7 +253,7 @@ Deep links work when the app is cold-started (not already running). The app will
## 🗺️ Roadmap
- [ ] **Native Wallet Integration**: Support for WalletConnect, MetaMask, and Coinbase Wallet.
-- [ ] **Smart Onboarding**: Social login and embedded wallets for non-crypto native users.
+- [x] **Smart Onboarding**: Social login and embedded wallets for non-crypto native users.
- [x] **Push Notifications**: Real-time alerts for role updates and access grants.
- [x] **QR Access Verification**: Scan GuildPass QR codes to verify token-gated resource access from the mobile app.
- [ ] **Offline Resilience**: Advanced caching layer for viewing memberships without connectivity.
diff --git a/docs/architecture.md b/docs/architecture.md
index c213802..9a8d5f0 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -65,6 +65,29 @@ declared in `src/lib/`, so the fan-out is readable, ordered, and awaitable in on
Adding a cross-feature transition means adding it to one of these modules, not importing
another feature's store into a hook or component.
+### Wallet providers
+
+Three wallet paths are supported, all converging through the `WalletConnector` interface
+into `useWalletStore`:
+
+| Path | Provider | Connection kind | How it works |
+|---|---|---|---|
+| **Manual entry** | None | `manual` | User pastes an EVM address; `createManualConnector` wraps it |
+| **WalletConnect** | WalletConnect v2 | `walletconnect` | WC modal → EIP-1193 → `createWalletConnectConnector` |
+| **Embedded wallet** | Privy (`@privy-io/expo`) | `embedded` | Email OTP or Google OAuth → Privy provisions MPC wallet → `createEmbeddedConnector` wraps the address |
+
+**Key design principle:** Privy is only the provisioning layer. Once the embedded wallet
+address enters `useWalletStore`, every downstream flow (memberships, guilds, access checks,
+sync, attestations) sees a standard EVM address. No screen or hook needs to know the wallet
+was provisioned by Privy.
+
+The embedded path is feature-flagged via `EXPO_PUBLIC_PRIVY_APP_ID` and
+`EXPO_PUBLIC_PRIVY_CLIENT_ID` environment variables. When both are set,
+`isEmbeddedWalletEnabled` is `true` and the onboarding screen offers the social/email option.
+
+See `docs/embedded-wallet-provider.md` for the provider evaluation, security model, and
+custody trade-off documentation.
+
### Selectors
Subscribe with a selector, never by calling the store hook bare. `useWalletStore()`
diff --git a/docs/embedded-wallet-provider.md b/docs/embedded-wallet-provider.md
new file mode 100644
index 0000000..b1cb52a
--- /dev/null
+++ b/docs/embedded-wallet-provider.md
@@ -0,0 +1,131 @@
+# Embedded Wallet Provider Evaluation
+
+> **Decision:** Privy (`@privy-io/expo`)
+> **Date:** 2026-07-27
+> **Status:** Adopted — integrated into `src/features/wallet/`
+
+## Evaluation Criteria
+
+| Criterion | Weight | Notes |
+|---|---|---|
+| React Native / Expo SDK maturity | Critical | Must have first-party Expo support; no bare-RN-only workarounds |
+| Social login providers | High | Email + Google required; Apple required for App Store |
+| Wallet provisioning model | High | Automatic on first login; no user ceremony |
+| Custody / key security model | High | Must NOT be fully custodial; MPC or TEE preferred |
+| Chain support | Medium | All EVM chains (GuildPass is EVM-only) |
+| Cost at launch scale | Medium | Free tier or low-cost for <1000 MAU |
+| Wallet export | Medium | Users should be able to extract their key if desired |
+
+---
+
+## Providers Evaluated
+
+### Privy ✅ (Selected)
+
+| Criterion | Assessment |
+|---|---|
+| **SDK** | `@privy-io/expo` — first-party, actively maintained, Expo-native. Already in `package.json` |
+| **Social login** | Email OTP, Google, Apple, Twitter, Discord, GitHub, Farcaster |
+| **Wallet provisioning** | `createOnLogin: "users-without-wallets"` — zero-ceremony Ethereum wallet creation |
+| **Custody model** | **MPC 2-of-3 key splitting**: device share (Secure Enclave), Privy infra share (HSM-backed), recovery share (encrypted to user's auth factor). No single party ever holds the full private key |
+| **Chain support** | All EVM chains |
+| **Cost** | Free: 1,000 MAU. Growth plan: usage-based pricing |
+| **Wallet export** | Supported — users can extract their full private key through the SDK |
+| **Recovery** | Re-authenticate with same email/social account on any device |
+
+**Why Privy:**
+1. Only provider with a first-party, production-ready Expo SDK (`@privy-io/expo`)
+2. MPC key model is the gold standard for embedded wallets — no seed phrase, no full custodial risk
+3. `createOnLogin` config eliminates all wallet ceremony
+4. Already integrated and partially wired in the codebase
+5. Auth-factor-based recovery means users can't lose access by losing a device
+
+### Web3Auth ❌
+
+| Criterion | Assessment |
+|---|---|
+| **SDK** | `@web3auth/react-native-sdk` — available but heavier; requires additional native module configuration that conflicts with Expo managed workflow |
+| **Social login** | Broad provider support |
+| **Custody model** | Threshold key splitting (similar to MPC) |
+| **Why not** | Heavier SDK footprint, more complex configuration, less Expo-native than Privy. Would require `expo prebuild` for native module linking |
+
+### Magic (now Magic.link) ❌
+
+| Criterion | Assessment |
+|---|---|
+| **SDK** | `@magic-sdk/react-native` — deprecated React Native SDK, limited maintenance |
+| **Why not** | Deprecated RN SDK with no Expo-specific support. Would require significant custom bridging |
+
+### Turnkey ❌
+
+| Criterion | Assessment |
+|---|---|
+| **SDK** | Lower-level API; no React Native SDK. Requires building custom auth + wallet UI |
+| **Why not** | Too low-level for a mobile app. Would require building the entire auth flow, wallet UI, and key management from scratch |
+
+### Particle Network ❌
+
+| Criterion | Assessment |
+|---|---|
+| **SDK** | `@particle-network/rn-auth` — available but less mature Expo support |
+| **Why not** | Less mature Expo support compared to Privy. Smaller developer community |
+
+---
+
+## Security Model
+
+### What Privy Protects Against
+
+- **Seed phrase loss/theft**: No seed phrase exists. Key shares are distributed across device, Privy infrastructure, and an auth-factor-encrypted recovery share.
+- **Device loss**: User re-authenticates with their email/social account on a new device to reconstruct their key.
+- **Single point of compromise**: MPC ensures no single party (not Privy, not the device) ever holds the full private key.
+
+### What Privy Does NOT Protect Against
+
+- **Privy infrastructure compromise**: If Privy's HSM-backed infrastructure is breached AND the attacker obtains the device share, the key could be reconstructed. This is a fundamental trust trade-off of any embedded wallet provider.
+- **Social account takeover + device theft**: An attacker who compromises both the user's auth factor (email/Google account) AND their device could potentially reconstruct the key.
+- **Nation-state actors**: Out of scope, consistent with the project's [threat model](./threat-model.md).
+
+### Alignment with GuildPass Threat Model
+
+The existing [threat model](./threat-model.md) already documents "Future: embedded private keys" as a **Critical** asset with planned storage in `expo-secure-store` / Secure Enclave. Privy's device share leverages exactly this: the device key share is stored in the platform's hardware-backed secure enclave (iOS Keychain / Android Keystore), aligned with the existing security architecture.
+
+---
+
+## Integration Architecture
+
+```
+┌─────────────────────────────────────────────────────┐
+│ Onboarding Screen │
+│ ┌─────────────────────────────────────────────────┐│
+│ │ CustodyDisclosure (trade-off explanation) ││
+│ │ Email OTP / Google OAuth sign-in ││
+│ └─────────────────────────────────────────────────┘│
+│ │ │
+│ ▼ │
+│ ┌─────────────────────────────────────────────────┐│
+│ │ Privy SDK (useLoginWithEmail / useLoginWithOAuth)│
+│ │ → auto-creates embedded Ethereum wallet ││
+│ │ → returns EVM address ││
+│ └─────────────────────────────────────────────────┘│
+│ │ │
+│ ▼ │
+│ ┌─────────────────────────────────────────────────┐│
+│ │ createEmbeddedConnector(address) ││
+│ │ → same WalletConnector interface as manual/WC ││
+│ └─────────────────────────────────────────────────┘│
+│ │ │
+│ ▼ │
+│ ┌─────────────────────────────────────────────────┐│
+│ │ useWalletStore ││
+│ │ walletAddress = "0x...", connectionKind = "embedded" │
+│ └─────────────────────────────────────────────────┘│
+│ │ │
+│ ▼ │
+│ All downstream flows (memberships, guilds, access │
+│ checks, sync, attestations) see a standard EVM │
+│ address — completely provider-agnostic. │
+└─────────────────────────────────────────────────────┘
+```
+
+Key design principle: **Privy is only the provisioning layer.** Once the embedded wallet address enters `useWalletStore`, every downstream flow treats it identically to a manually-entered or WalletConnect address. No screen, hook, or service needs to know the wallet was provisioned by Privy.
diff --git a/src/features/wallet/CustodyDisclosure.tsx b/src/features/wallet/CustodyDisclosure.tsx
new file mode 100644
index 0000000..4ee6b9a
--- /dev/null
+++ b/src/features/wallet/CustodyDisclosure.tsx
@@ -0,0 +1,102 @@
+import React, { useState } from "react";
+import { View, Text, TouchableOpacity, Linking } from "react-native";
+
+const PRIVY_SECURITY_URL = "https://docs.privy.io/guide/security";
+
+/**
+ * Custody disclosure shown during embedded-wallet onboarding.
+ *
+ * Renders a summary line that is always visible, with a collapsible detail
+ * section the user can expand. This satisfies the acceptance criterion of
+ * "clear, honest in-app explanation" without forcing a blocking modal that
+ * would hurt conversion.
+ */
+export function CustodyDisclosure({ testID = "custody-disclosure" }: { testID?: string }) {
+ const [expanded, setExpanded] = useState(false);
+
+ return (
+
+ {/* ── Always-visible summary ─────────────────────────────────── */}
+ setExpanded((prev) => !prev)}
+ accessibilityRole="button"
+ accessibilityLabel={expanded ? "Collapse wallet details" : "Expand wallet details"}
+ accessibilityState={{ expanded }}
+ testID="custody-disclosure-toggle"
+ activeOpacity={0.7}
+ >
+
+
+ 🔐
+
+ About your embedded wallet
+
+
+
+ {expanded ? "Hide ▲" : "Details ▼"}
+
+
+
+ A secure wallet will be created for you automatically — no seed phrase needed.
+
+
+
+ {/* ── Expanded detail section ────────────────────────────────── */}
+ {expanded ? (
+
+
+
+
+
+
+
+
+
+ Linking.openURL(PRIVY_SECURITY_URL)}
+ accessibilityRole="link"
+ accessibilityLabel="Learn more about Privy security"
+ testID="custody-disclosure-learn-more"
+ className="mt-2"
+ >
+
+ Learn more about Privy's security model →
+
+
+
+ ) : null}
+
+ );
+}
+
+function DetailItem({ emoji, title, body }: { emoji: string; title: string; body: string }) {
+ return (
+
+
+ {emoji} {title}
+
+
+ {body}
+
+
+ );
+}
diff --git a/src/features/wallet/EmbeddedWalletOnboarding.tsx b/src/features/wallet/EmbeddedWalletOnboarding.tsx
index 1c07c4a..52b8474 100644
--- a/src/features/wallet/EmbeddedWalletOnboarding.tsx
+++ b/src/features/wallet/EmbeddedWalletOnboarding.tsx
@@ -7,6 +7,7 @@ import {
usePrivy,
} from "@privy-io/expo";
import { Button } from "../../components/Button";
+import { CustodyDisclosure } from "./CustodyDisclosure";
import { useWallet } from "./useWallet";
type Props = { onComplete(): void; onBack(): void };
@@ -81,6 +82,7 @@ export function EmbeddedWalletOnboarding({ onComplete, onBack }: Props) {
Sign in with email or Google. A secure wallet will be created for you automatically.
+