mallory
mallory copied to clipboard
Opinionated React Native crypto x AI chat app boilerplate with embedded wallet support, conversational AI, and dynamic UI component injection
Mallory
Opinionated React Native crypto x AI chat app boilerplate with embedded wallet support, conversational AI, and dynamic UI component injection.
🏗️ Monorepo Structure
mallory/
├── apps/
│ ├── client/ # React Native app (iOS, Android, Web)
│ └── server/ # Backend API (Node.js + Express)
├── packages/
│ └── shared/ # Shared types and utilities
└── package.json # Workspace configuration
✨ Features
Client (Mobile & Web)
- 🔐 Authentication: Google OAuth via Supabase
- 💬 AI Chat: Streaming conversations with Claude
- 💰 Embedded Wallet: Grid-powered smart contract wallets
- 🔑 Client-Side Signing: Secure transaction signing (keys never leave device)
- 📱 Cross-Platform: iOS, Android, and Web from single codebase
- 🎨 Modern UI: Beautiful, responsive design with Reanimated
- 🏷️ Version Tracking: Automatic version display with git commit hash
Server (Backend API)
- 🤖 AI Streaming: Claude integration with Server-Sent Events and extended thinking
- 🔧 AI Tools: Web search (Exa), user memory (Supermemory), and 20+ Nansen data APIs
- 💰 x402 Payments: Server-side implementation for premium data access
- 💎 Wallet Data: Price enrichment via Birdeye API
- 🔒 Secure Auth: Supabase JWT validation
- 🚀 Production Ready: Comprehensive testing infrastructure
Monorepo Management
- 🔄 Synchronized Versioning: Single command updates all packages
- 🏷️ Automatic Releases: GitHub releases created on version tags
- 📝 Generated Changelogs: Commit history automatically compiled
🚀 Quick Start
Prerequisites
- Node.js 18+ or Bun
- Git
- Expo CLI (optional, included in dependencies)
1. Clone and Install
git clone https://github.com/darkresearch/mallory.git
cd mallory
bun install
2. Environment Setup
Client Environment (.env in apps/client/)
# Copy from template
cp apps/client/.env.example apps/client/.env
# Required variables:
EXPO_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
EXPO_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
EXPO_PUBLIC_BACKEND_API_URL=http://localhost:3001
EXPO_PUBLIC_GRID_API_KEY=your-grid-api-key
EXPO_PUBLIC_GRID_ENV=sandbox
Server Environment (.env in apps/server/)
# Copy from template
cp apps/server/.env.example apps/server/.env
# Required variables:
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
ANTHROPIC_API_KEY=sk-ant-your-key
BIRDEYE_API_KEY=your-birdeye-key
GRID_API_KEY=your-grid-api-key
# Optional (for AI tools):
EXA_API_KEY=your-exa-key
# Infinite Memory (OpenMemory for infinite context):
OPENMEMORY_URL=http://localhost:8080
OPENMEMORY_API_KEY=your-openmemory-key
3. Run Development Servers
Option A: Run Both (Client + Server)
bun run dev
Option B: Run Separately
# Terminal 1 - Backend
bun run server
# Terminal 2 - Client (Web)
bun run client
The client will be available at:
- Web: http://localhost:8081
- API: http://localhost:3001
📱 Client Development
See apps/client/README.md for detailed client documentation.
Key Commands:
cd apps/client
# Web
bun run web
# iOS (requires Mac + Xcode)
bun run ios
# Android (requires Android Studio)
bun run android
🔧 Server Development
See apps/server/README.md for detailed server documentation.
API Endpoints:
-
POST /api/chat- AI chat streaming with tool calling -
GET /api/wallet/holdings- Wallet holdings with price data -
GET /health- Health check
AI Tools:
-
searchWeb- Web search via Exa (always available) -
addMemory- User memory via Supermemory (optional) -
nansen*- 20+ Nansen API endpoints for blockchain analytics (requires x402 payments)
🔑 Grid Wallet Integration
Mallory uses Grid for embedded wallets:
- Non-Custodial: User private keys never exist - Grid uses secure enclaves and MPC
- Email-Based Auth: Simple OTP verification flow
- Session Secrets: Generated client-side, passed to backend only when needed for signing
- Smart Contract Wallets: Spending limits and programmable transactions
- Production Ready: Sandbox and production environments
- x402 Integration: Automatic micropayments for premium data APIs
Grid's architecture means neither the client nor server ever has access to user private keys, making it truly non-custodial while still providing seamless transaction signing.
📦 Shared Package
The packages/shared directory contains TypeScript types and utilities shared between client and server:
import type { ChatRequest, HoldingsResponse } from '@darkresearch/mallory-shared';
import { X402PaymentService } from '@darkresearch/mallory-shared';
🧪 Testing
Mallory has comprehensive test coverage: unit tests, integration tests, and E2E tests.
Run tests:
cd apps/client
# Fast tests (unit + integration)
bun test
# E2E tests (requires backend running)
bun run test:e2e
# AI-powered tests (optional - expensive)
# These use Claude to verify response completeness and test 200k+ token conversations
bun test __tests__/e2e/chat-message-flow.test.ts # ~5-10 min, ~$1-2
bun test __tests__/e2e/long-context.test.ts # ~10-20 min, ~$2-3
CI/CD:
- Regular tests run on every PR
- AI tests only run when
[run-ai-tests]is in commit message:git commit -m "fix: improve streaming [run-ai-tests]"
See apps/client/tests/CHAT_STATE_TESTS.md for full testing documentation.
🚢 Deployment
Client Deployment
- Web: Deploy to Vercel, Netlify, or any static host
- iOS: Deploy via Expo EAS or native build
- Android: Deploy via Expo EAS or native build
See apps/client/README.md for details.
Server Deployment
- Recommended: Railway, Render, Fly.io
- Node.js: Any Node.js 18+ hosting
See apps/server/README.md for details.
🛠 Troubleshooting
Google OAuth “Unsupported provider” — Full Setup Guide (Web + Expo RN)
The 400 response means Supabase doesn’t know about the Google provider yet. Here’s the full fix, covering both the browser login flow and the native Expo app.
1. Enable Google in Supabase
- Go to Supabase dashboard → your project → Authentication → Providers → Google.
- Toggle Enable on. Supabase now expects a Google Client ID/secret.
Make a note of the Redirect URL shown at the top (it looks like
https://<project-ref>.supabase.co/auth/v1/callback). You’ll paste that into Google Cloud Console in a moment.
2. Create Google OAuth credentials
- Open https://console.cloud.google.com/apis/credentials (select the project you’ll use for Mallory).
- Ensure the OAuth consent screen is configured (app name, support email, scopes, test users).
- Click Create credentials → OAuth client ID.
- Choose Web application (works for both the web and native flows Mallory uses).
- Fill in:
-
Authorized redirect URIs: add the Supabase callback from step 1, e.g.
https://<project-ref>.supabase.co/auth/v1/callback -
Authorized JavaScript origins aren’t required for Supabase, but you can add your local web origin (e.g.
http://localhost:8081) if you’re running the web client.
-
Authorized redirect URIs: add the Supabase callback from step 1, e.g.
- Save. Copy the generated Client ID and Client secret.
What about
exp://…?
That kind of URI applies when you’re using Expo’s WebView-based AuthSession. Mallory’s native flow uses@react-native-google-signin/google-signin, which talks directly to Google Play Services and does not need (or accept) anexp://URI. So you can skip adding exp:// schemes here.
3. Paste credentials back into Supabase
Still on the Google provider page in Supabase:
- Paste the Client ID and Client secret you just copied.
- Click Save. Supabase now accepts Google logins.
4. Update Mallory’s configuration (web + Expo)
Whether you’re running the browser app or the Expo-native build, you need the same Google client ID available at runtime.
- Edit
apps/client/.env(or wherever you manage env vars) and set:
Mallory’s native layer uses the “Web application” client for Android; for iOS you can optionally create an iOS-specific client and set it here.EXPO_PUBLIC_GOOGLE_ANDROID_CLIENT_ID=<the web client ID from Google> EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID=<optional iOS client ID if you generated one> - If you’re running the web client, also ensure the same ID is present; the
config.tsmodule pulls from theseEXPO_PUBLIC_*variables. - Restart Expo / Metro (and rebuild Android/iOS if needed) so the new env values take effect.
5. Test both surfaces
- Web: open the browser client, click Continue with Google, finish the Google OAuth flow; you should be redirected to Supabase, then back into Mallory with a logged-in session.
-
Expo + Android/iOS: rebuild (
bun run androidorbun run ios), trigger Continue with Google. The native Google Sign-In dialog should appear. After you grant access, Supabase will issue the session tokens and Mallory should log you in.
TL;DR on local dev URLs
-
Web (browser): your “local dev URL” is the origin where your web app runs (e.g.
http://localhost:8081). Add it under “Authorized JavaScript origins” if you need to load Google scripts directly (not strictly required for Supabase-based OAuth, but harmless). -
Expo React Native: no
exp://redirect URI is required for the Google Sign-In library Mallory uses. Just ensure the web client ID is configured and Supabase has the provider enabled. Once those steps are done, the “Unsupported provider: provider is not enabled” error disappears for both web and native runs.
🏷️ Version Management
Mallory uses synchronized semantic versioning across all packages.
Auto-Release via PR
Include [release: v*.*.*] in your PR title:
feat: add new wallet feature [release: v0.2.0]
When merged to main, the version automatically bumps and a GitHub release is created! 🚀
Manual Release
bun scripts/sync-version.js 0.2.0
git add . && git commit -m "chore: bump version to 0.2.0"
git tag v0.2.0 && git push && git push --tags
See VERSION.md for details.
🤝 Contributing
Contributions welcome! Please read CONTRIBUTING.md first.
📄 License
Apache License 2.0 - see LICENSE for details.
🆘 Support
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 📚 Docs: Full Documentation
🙏 Acknowledgments
Built with:
- Expo - React Native framework
- Grid (Squads) - Embedded wallets
- Anthropic - Claude AI with extended thinking
- Exa - AI-powered web search
- Supermemory - User memory & RAG
- Supabase - Auth & database
- Birdeye - Solana market data
- Nansen - Blockchain analytics (via x402)
- Faremeter - x402 payment protocol
- streamdown-rn - React Native markdown streaming
Made with ❤️ by Dark