Skip to main content

Architecture Overview

Rainbow is built with a modern, scalable architecture that prioritizes type safety, performance, and developer experience.

State Management

Rainbow uses custom store creators built on top of Zustand, providing a unified approach to state management across the application.

Store Types

Rainbow Store

General-purpose store with optional MMKV persistence for client state

Query Store

Combines data fetching + state in one store with reactive parameters

Derived Store

Read-only store that composes other stores for computed state

Rainbow Store (Client State)

General-purpose store with optional MMKV persistence. Use for client-side state that needs to persist across app restarts.
import { createRainbowStore } from '@/state/internal/createRainbowStore';

const useSettingsStore = createRainbowStore({
  // Store definition with optional MMKV persistence
});

Query Store (Server State)

Combines data fetching and state management. Reactive $ parameters automatically refetch when dependencies change.
import { createQueryStore } from '@/state/internal/createQueryStore';

const useDataStore = createQueryStore({
  // Replaces React Query + Zustand dual-store pattern
});
These in-repo creators are being replaced by the external stores package with the same APIs (createBaseStore, createQueryStore, createDerivedStore).

Derived Store (Computed State)

Read-only store that composes other stores for aggregated or computed state.
import { createDerivedStore } from '@/state/internal/createDerivedStore';

const useComputedStore = createDerivedStore({
  // Combines multiple stores
});

Legacy State Systems

Some legacy systems are still in use during the migration:
  • React Query (src/react-query/) - Server state caching, being replaced by createQueryStore
  • Redux (src/redux/) - Only used for: charts, contacts, ENS registration, gas, and settings

Source Code Organization

The codebase is mid-migration toward domain-organized architecture.

New Structure (Feature-Based)

New code goes in src/features/ with clear layer separation:
src/features/
  ├── positions/
  │   ├── ui/          # React components
  │   ├── data/        # Data fetching and state
  │   └── core/        # Business logic
  └── swaps/
      ├── ui/
      ├── data/
      └── core/

Legacy Structure (Flat)

Legacy code lives in flat top-level directories:
  • components/ - Shared React components
  • screens/ - Screen-level components
  • hooks/ - Custom React hooks
  • helpers/ - Helper functions
  • utils/ - Utility functions

Key Directories

App-agnostic infrastructure including:
  • HTTP client
  • Safe math utilities
  • UI primitives
  • Core utilities
Swap feature implementation
  • Aliased as @/swaps in tsconfig
  • Self-contained feature module
GraphQL codegen workspace
  • Separate yarn workspace
  • Generated types and queries
Domain-specific stores
  • One store per domain
  • Custom store creators

Code Conventions

Rainbow enforces strict code conventions for maintainability and performance.

No Barrel Exports

Import directly from source files, not index.ts. Barrel exports defeat tree-shaking, hide circular dependencies, and trigger cascading module loading.
// ❌ Bad - barrel export
import { Component } from '@/components';

// ✅ Good - direct import
import { Component } from '@/components/Component';
This is ESLint-enforced with a limited allowlist.

Type-Only Imports

Use the type annotation for type-only imports (ESLint-enforced):
import type { User } from '@/types/User';
import { fetchUser } from '@/api/users';

TypeScript Over JavaScript

Write all new files in .ts/.tsx.
  • Remaining JS files are checked against an error baseline
  • Run yarn lint:js-types to check JavaScript type errors
  • Don’t regress the error baseline

Tech Stack

Core Technologies

TechnologyVersionPurpose
React Native0.79.5Mobile app framework
React19.0.0UI library
TypeScript5.9.3Type safety
Node.js22+JavaScript runtime

State & Data

  • Zustand (4.5.5) - State management foundation
  • @storesjs/stores (0.8.7) - Custom store creators
  • @tanstack/react-query (4.2.1) - Server state (legacy)
  • React Redux (7.2.1) - Redux integration (legacy)

Blockchain

  • viem (2.38.0) - Ethereum interactions
  • ethers.js (5.8.0) - Ethereum library
  • @rainbow-me/swaps - Swap functionality
  • @walletconnect - WalletConnect integration

UI & Animation

  • @shopify/react-native-skia - Canvas rendering
  • react-native-reanimated (3.19.4) - Animations
  • @shopify/flash-list (1.8.2) - Performant lists
  • expo (53.0.20) - Expo modules
  • @react-navigation/stack - Stack navigation
  • @react-navigation/material-top-tabs - Tab navigation

Verification Commands

Rainbow provides comprehensive verification tools:
# Type check TypeScript
yarn lint:ts

# Type check JavaScript against baseline
yarn lint:js-types

# ESLint
yarn lint:js

# All linting (format + TS + JS)
yarn lint

# Run tests
yarn test

# Run specific test
yarn jest path/to/test

# Check for circular dependencies
yarn check:cycles

Performance Features

Flash List

High-performance lists with @shopify/flash-list

Skia Rendering

Native rendering with React Native Skia

Reanimated

60fps animations on the native thread

MMKV Storage

Fast, synchronous storage with react-native-mmkv

Testing

Rainbow uses multiple testing strategies:

Unit Testing

  • Jest for unit tests
  • Run with yarn test
  • Located throughout the codebase

E2E Testing

  • Maestro for end-to-end tests
  • iOS and Android support
  • Located in e2e/ directory
  • See the Debugging Guide for E2E testing details

Type Coverage

Rainbow maintains 99%+ TypeScript coverage:
yarn ts-coverage

Next Steps

Setup Guide

Get your development environment ready

Running the App

Learn how to run Rainbow locally

Building

Build for production

Debugging

Debug and troubleshoot issues

Build docs developers (and LLMs) love