Skip to content

Latest commit

Β 

History

51 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Rayos Wallet

Rayos β€” Mobile Wallet

Passkey-secured, seedless smart wallet for iOS & Android

Built on Stellar Β· Powered by Soroban smart contracts


Expo SDK 57 React Native 0.86 TypeScript TanStack Query Stellar EAS CI License MIT


πŸ€– Download Android APK/AAB Β Β·Β  🌐 Web Dashboard Β Β·Β  πŸ“– Docs Β Β·Β  πŸ› Report Bug Β Β·Β  ✨ Request Feature

πŸš€ Preview Build (ANDROID ONLY): Download Android App (AAB) Note: This build artifact is valid for 29 days from compilation. iOS preview builds are currently unsupported as they require a paid Apple Developer account.


What is Rayos?

Rayos is a seedless, self-custodial smart wallet on the Stellar network. There are no seed phrases β€” your keys live in your device's Secure Enclave (iOS) or StrongBox (Android), protected by Face ID, Touch ID, or biometrics. The on-chain wallet is a Soroban smart contract that verifies WebAuthn signatures directly, meaning you get the security of passkeys with the programmability of smart contracts.

This repository is the native mobile app β€” the most polished way to experience the passkey UX. It is feature-equivalent to the web-dashboard but passkeys on a real phone, unlocked by your face, are where the "no seed phrase" pitch really lands.


The Rayos Ecosystem

This app is one piece of a larger open-source system. Here's how the repos connect:

Repo Role How mobile uses it
mobile-app ← you are here Native iOS & Android app β€”
wallet-sdk Shared TypeScript SDK Vendored tarball in vendor/ (pnpm sdk:vendor to refresh) β€” WalletSdk class, PasskeyProvider interface, all types
relay-backend NestJS gasless relay API All HTTP calls: WebAuthn, relay, sessions, recovery, .well-known files
wallet-contracts Soroban smart contracts (Rust) Indirect β€” JS bindings consumed through wallet-sdk
web-dashboard Next.js web app Design token parity; same conceptual API contract
infra GitHub Actions, Docker, env specs EAS build workflow reused; env variable naming convention

Features

Feature Details
πŸ”‘ Passkey Wallet Creation Register with Face ID / Touch ID β€” no seed phrase, no password. Deploys a Soroban smart contract on Stellar automatically.
πŸ” Native Biometric Signing Every transaction is approved by your biometric. The passkey never leaves the device Secure Enclave.
πŸ’Έ Send & Receive XLM Send Stellar assets with a biometric confirmation. Transaction history pulled live from Horizon.
πŸ›‘οΈ Spend Limits Set rolling per-token spend limits enforced on-chain by the Policy contract.
πŸ”‘ Session Keys Create scoped, time-limited session keys for specific dapps or automations.
πŸ‘₯ Social Recovery Add guardian addresses. If you lose your device, 2-of-N guardians can approve a new passkey β€” with a 48-hour timelock.
πŸ“² Deep Link Recovery Approval Guardians receive an email link; tapping it on mobile opens the app directly on the approval screen.
πŸŒ™ System / Light / Dark Theme Follows OS by default; user can override in Settings. Choice is persisted securely.
πŸ“‘ Offline-first TanStack Query with networkMode: offlineFirst β€” pending state is clear, retries happen automatically.

Repository Structure

mobile-app/
β”‚
β”œβ”€β”€ app/                              ← expo-router file-based routes
β”‚   β”œβ”€β”€ _layout.tsx                   ← Root: QueryClient, deep-link handler, auth gate
β”‚   β”œβ”€β”€ +not-found.tsx
β”‚   β”œβ”€β”€ (onboarding)/                 ← Unauthenticated group (Stack)
β”‚   β”‚   β”œβ”€β”€ index.tsx                 ← Welcome / landing screen
β”‚   β”‚   β”œβ”€β”€ create.tsx                ← Passkey registration β†’ wallet deployment
β”‚   β”‚   β”œβ”€β”€ login.tsx                 ← Passkey assertion β†’ wallet lookup
β”‚   β”‚   └── recover.tsx               ← Initiate guardian recovery
β”‚   β”œβ”€β”€ (dashboard)/                  ← Authenticated group (Tabs)
β”‚   β”‚   β”œβ”€β”€ wallet.tsx                ← Balance Β· send Β· receive Β· activity
β”‚   β”‚   β”œβ”€β”€ policies.tsx              ← Spend limit Β· session keys Β· allow-list
β”‚   β”‚   β”œβ”€β”€ guardians.tsx             ← Guardians Β· threshold Β· active recovery
β”‚   β”‚   └── settings.tsx             ← Theme Β· app info Β· sign out
β”‚   └── recovery/[proposalId].tsx     ← Guardian approval (deep-link target)
β”‚
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ ui/                           ← Design system: Button, Card, Input, Sheet, Toast…
β”‚   β”œβ”€β”€ layout/                       ← TabBar, ThemeSwitch, OnboardingHeader, StepDots
β”‚   β”œβ”€β”€ wallet/                       ← BalanceCard, TransactionList, SendSheet, SignerList
β”‚   β”œβ”€β”€ policies/                     ← SpendLimitCard, SessionKeysCard, AllowListCard
β”‚   └── guardians/                    ← GuardianList, RecoveryBanner, RecoveryStatusCard
β”‚
β”œβ”€β”€ native/
β”‚   └── passkey-adapter.ts            ← β˜… ONLY platform-specific seam
β”‚                                         Implements PasskeyProvider (react-native-passkeys)
β”‚
β”œβ”€β”€ hooks/
β”‚   β”œβ”€β”€ useTheme.tsx                  ← Color-scheme resolution + SecureStore override
β”‚   β”œβ”€β”€ useWallet.ts                  ← Balance, signers, send mutation
β”‚   β”œβ”€β”€ usePolicies.ts                ← Session keys CRUD
β”‚   └── useRecovery.ts                ← Recovery proposal lifecycle (10s polling)
β”‚
β”œβ”€β”€ lib/
β”‚   β”œβ”€β”€ config.ts                     ← Zod-validated EXPO_PUBLIC_* env vars
β”‚   β”œβ”€β”€ sdk-client.ts                 ← WalletSdk singleton (native passkeyProvider injected)
β”‚   β”œβ”€β”€ storage-adapter.ts            ← StorageAdapter β†’ expo-secure-store
β”‚   β”œβ”€β”€ query-client.ts               ← TanStack Query (offline-first defaults)
β”‚   β”œβ”€β”€ api.ts                        ← Typed fetch helpers for relay-backend
β”‚   └── format.ts                     ← XLM, address, date formatting
β”‚
β”œβ”€β”€ store/auth.ts                     ← Zustand (walletAddress + credentialId, SecureStore-persisted)
β”œβ”€β”€ assets/                           ← Icons, splash, logo β€” light & dark variants
β”œβ”€β”€ e2e/flows/                        ← Maestro YAML E2E flows
β”‚
β”œβ”€β”€ docs/                             ← πŸ“– Extended documentation
β”‚   β”œβ”€β”€ ARCHITECTURE.md               ← Deep-dive: design decisions, flows, CI/CD
β”‚   β”œβ”€β”€ SETUP.md                      ← Step-by-step local dev + real-device setup
β”‚   β”œβ”€β”€ CONTRIBUTING.md               ← How to contribute, code standards, commit convention
β”‚   └── SECURITY.md                   ← Security model + vulnerability reporting
β”‚
β”œβ”€β”€ .github/
β”‚   β”œβ”€β”€ workflows/                    ← ci Β· eas-preview Β· eas-release Β· eas-update
β”‚   β”œβ”€β”€ ISSUE_TEMPLATE/               ← Bug report Β· Feature request (GitHub Forms)
β”‚   └── PULL_REQUEST_TEMPLATE.md
β”‚
β”œβ”€β”€ app.config.ts                     ← Expo config (env-driven, no secrets)
β”œβ”€β”€ eas.json                          ← development / preview / production EAS profiles
└── .env.example                      ← All EXPO_PUBLIC_* vars β€” copy to .env

User Workflow

flowchart TD
    A([Open App]) --> B{Has Session?}
    B -- No --> C[Welcome Screen]
    B -- Yes --> DASH

    C --> D{New or Returning?}
    D -- New User --> E[Enter Display Name]
    D -- Returning --> F[Login with Passkey]

    E --> G["Face ID / Touch ID Prompt<br/>create passkey"]
    G --> H["Relay: /webauthn/register/options<br/>+ /webauthn/register/verify"]
    H --> I["Deploy Wallet Contract<br/>on Stellar Testnet"]
    I --> DASH

    F --> J["Face ID / Touch ID Prompt<br/>assert passkey"]
    J --> K["Relay: GET /wallets/:credentialId<br/>lookup wallet address"]
    K --> DASH

    DASH([Dashboard]) --> TAB1["πŸ’° Wallet Tab"]
    DASH --> TAB2["πŸ›‘οΈ Policies Tab"]
    DASH --> TAB3["πŸ‘₯ Guardians Tab"]

    TAB1 --> L["View XLM Balance<br/>+ Transaction History"]
    TAB1 --> M["Send XLM<br/>Biometric sign β†’ relay submit"]

    TAB2 --> N[View Session Keys]
    TAB2 --> O["Revoke Session Key<br/>DELETE /api/sessions/:id"]

    TAB3 --> P{Active Recovery?}
    P -- Yes --> Q["View Status<br/>+ Approve Recovery<br/>POST /api/recovery/approve"]
    P -- No --> R["Initiate Recovery<br/>POST /api/recovery/propose"]

    Z(["Deep Link<br/>rayos://recovery/:id"]) --> Q
Loading

System Architecture

graph TB
    subgraph "Mobile App (this repo)"
        APP["expo-router screens"]
        HOOKS["hooks/<br/>useWallet Β· usePolicies Β· useRecovery"]
        ADAPTER["native/passkey-adapter.ts<br/>PasskeyProvider impl"]
        SDK_CLIENT["lib/sdk-client.ts<br/>WalletSdk singleton"]
        STORE["store/auth.ts<br/>Zustand + SecureStore"]
        APP --> HOOKS
        HOOKS --> SDK_CLIENT
        SDK_CLIENT --> ADAPTER
        HOOKS --> STORE
    end

    subgraph "wallet-sdk (file dep)"
        WSDK[WalletSdk]
        WCLIENT["WalletClient<br/>Soroban contract calls"]
        PCLIENT["PolicyClient<br/>Sessions Β· Recovery"]
        RCLIENT["RelayClient<br/>HTTP relay calls"]
        WSDK --> WCLIENT
        WSDK --> PCLIENT
        WSDK --> RCLIENT
    end

    subgraph "relay-backend (NestJS)"
        WA["/webauthn/*<br/>Challenge + verify"]
        REL["/relay/submit<br/>+ /relay/status"]
        SES["/sessions<br/>Session key CRUD"]
        REC["/recovery/*<br/>Propose + approve"]
        IDX["/wallets/:credentialId<br/>Address lookup"]
        WK["/.well-known/*<br/>AASA + assetlinks"]
    end

    subgraph "Stellar Network"
        SRPC[Soroban RPC]
        HRZ["Horizon API<br/>Transaction history"]
        FACTORY[FactoryContract]
        WALLET_C[WalletContract]
        POLICY_C[PolicyContract]
    end

    subgraph "Device"
        ENCLAVE["Secure Enclave / StrongBox<br/>Passkey never leaves here"]
    end

    SDK_CLIENT --> WSDK
    ADAPTER --> ENCLAVE
    RCLIENT --> REL
    RCLIENT --> WA
    WCLIENT --> SRPC
    SRPC --> FACTORY
    SRPC --> WALLET_C
    SRPC --> POLICY_C
    HOOKS -->|Horizon fetch| HRZ
    HOOKS --> IDX
    HOOKS --> SES
    HOOKS --> REC
Loading

Getting Started

# 1. Clone wallet-sdk alongside (required β€” file: dependency)
git clone https://github.com/Rayos-Org/wallet-sdk.git
cd wallet-sdk && pnpm install && pnpm build && cd ..

# 2. Clone and install this app
git clone https://github.com/Rayos-Org/mobile-app.git
cd mobile-app
pnpm install

# 3. Configure environment
cp .env.example .env

# 4. Start dev server
pnpm ios        # Xcode simulator
pnpm android    # Android emulator
pnpm start      # Metro only (for use with Expo Dev Client)

⚠️ Expo Go will not work. Passkeys require the native module from react-native-passkeys. Use a development build: pnpm build:dev (EAS) or pnpm ios / pnpm android locally.

β†’ Full setup guide (real device, passkeys, EAS): docs/SETUP.md


Testing

pnpm typecheck      # TypeScript strict check
pnpm lint           # ESLint (eslint-config-expo)
pnpm format:check   # Prettier
pnpm test           # Jest unit + component tests
pnpm test:ci        # Jest with coverage report
pnpm e2e            # Maestro E2E flows (device/simulator must be running)

What's Tested

Layer Coverage
lib/format.ts XLM formatting, address truncation, date helpers
lib/api.ts Error normalisation, typed responses
lib/theme.ts Token resolution in both color schemes
native/passkey-adapter.ts Output shape matches PasskeyCredential / PasskeyAssertion types
store/auth.ts Zustand store mutations and persistence
components/ Render in light + dark theme; interaction snapshots
e2e/flows/onboarding.yml Full onboarding flow to wallet screen
e2e/flows/send.yml Send flow up to biometric gate

Biometric ceremonies themselves are exercised in manual device-lab testing before every release.


CI / CD

Workflow Trigger What it does
ci.yml Every PR + main push typecheck β†’ lint β†’ prettier β†’ jest β†’ expo-doctor β†’ expo export
eas-preview.yml Every PR + main push eas build --profile preview Β· posts QR code comment on PR
eas-release.yml Push tag v*.*.* quality gates β†’ production build β†’ ⏸ manual approval β†’ eas submit
eas-update.yml main push or manual dispatch eas update OTA to preview or production channel
PR ──▢ ci.yml ──▢ eas-preview.yml (installable QR build)
main ──▢ eas-update.yml (OTA to preview channel)
tag v1.x.x ──▢ eas-release.yml ──▢ ⏸ manual approval ──▢ eas submit

Documentation

Document Description
docs/ARCHITECTURE.md Deep-dive: tech stack, directory structure, design decisions, all key flows
docs/SETUP.md Step-by-step: local dev, real device setup, passkey configuration, EAS first-time setup
docs/CONTRIBUTING.md How to contribute: commit convention, PR process, code standards, testing requirements
docs/SECURITY.md Security model, responsible disclosure process

Contributing

We welcome contributions of all kinds β€” bug reports, feature ideas, code, tests, documentation, design feedback.

Quick start:

  1. Read docs/CONTRIBUTING.md
  2. Check open issues for good first issue labels
  3. Fork β†’ branch β†’ PR against main

Found a security issue? Please use GitHub Security Advisories instead of a public issue. See docs/SECURITY.md.


License

MIT Β© 2026 Rayos Org contributors.


Built with ❀️ by the Rayos community.

⭐ Star this repo if you find it useful β€” it helps others discover the project.

GitHub stars GitHub forks

About

The native mobile experience, focused on the smoothest possible passkey UX (Face ID / Touch ID / Android biometric prompts feel far more native here than in a mobile browser

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages