A non-custodial wallet for iOS and Android — one seed across EVM, Solana and Bitcoin, with swaps.
OpenWallet Mobile is a React Native port of OpenWallet, carrying over its chain core — a BIP-39 seed, SLIP-0010/BIP-32 derivation, an encrypted-at-rest vault, and per-chain signing behind a single ChainAdapter contract — and dropping everything that needed a server. It runs without one: prices come straight from CoinGecko and swaps straight from LI.FI on EVM and Jupiter on Solana, each over its keyless public tier, with optional API keys in Settings for a higher rate limit. Key material exists only inside withPrivateKey callbacks in the wallet layer and is zeroed when they settle; screens reach the wallet through one facade and never hold a key, a Wallet, or a chain package. The KDF runs full-strength scrypt (N=2¹⁸) through a native OpenSSL binding, because the same parameters in pure JavaScript cost roughly a minute per derivation on Hermes.
Nothing in this repository has been audited. Test networks ship alongside the main ones and are labelled as such.
| Area | Technology |
|---|---|
| Language | TypeScript (Node.js ≥ 20, strict, type-aware ESLint) |
| App | Expo SDK 57 + React Native 0.86, React 19, React Navigation |
| Cryptography | @noble/* / @scure/* |
| KDF | react-native-quick-crypto — native scrypt |
| EVM | viem — signing, ERC-20, fee tiers, ENS |
| Solana | @solana/web3.js — SPL, priority fees |
| Bitcoin | bitcoinjs-lib — native SegWit, PSBT, RBF |
| Prices | CoinGecko, called directly |
| Swaps | LI.FI on EVM, Jupiter on Solana, called directly |
| Storage | AsyncStorage for the encrypted vault and app settings |
| Tooling | pnpm workspaces, Vitest, Prettier |
flowchart LR
A[BIP-39 mnemonic] --> B[SLIP-0010 / BIP-32 derivation]
B --> C[Encrypted vault]
C --> D{ChainAdapter}
D --> E[EVM chains]
D --> F[Solana]
D --> G[Bitcoin]
H[Screens] -->|walletApi| D
D --> I[JSON-RPC nodes]
J[CoinGecko] -->|prices| D
- A mnemonic is generated or imported, then encrypted into a vault that only decrypts with the user's password — the plaintext seed never touches disk, and the password is never persisted, so a cold start always locks.
- Accounts are derived per chain from that one seed (secp256k1 for EVM and Bitcoin, ed25519 for Solana), each following its chain's SLIP-44 path convention.
- Every chain package exposes the same
ChainAdapter: derive, validate, quote fees, build, sign, broadcast. Adding a chain doesn't touch the send flow. - The extension split this across a background worker and a popup over a message protocol. A React Native app is one process, so the protocol is gone — but the seam it enforced is kept: screens import
wallet-api.tsand nothing below it, which makes "where can key material appear" a question answered by reading imports.
| Package | What it is |
|---|---|
@openwallet/core |
Mnemonic, HD/SLIP-10 derivation, encrypted vault, keyring, ChainAdapter |
@openwallet/chain-evm |
EVM derivation, signing, ERC-20 transfers, fee tiers, ENS, speed-up/cancel |
@openwallet/chain-solana |
Solana derivation, signing, SPL transfers, fee tiers |
@openwallet/chain-bitcoin |
Bitcoin (native SegWit) derivation, PSBT signing, RBF, message signing |
@openwallet/swap-contract |
The shapes every aggregator client returns, so the wallet holds no provider-specific parsing |
@openwallet/lifi-client |
LI.FI quotes and token registry for EVM swaps |
@openwallet/jupiter-client |
Jupiter quotes and token search for Solana swaps |
@openwallet/mobile |
The Expo app: onboarding, accounts, networks, send, receive, swap, settings |
Dependency direction is chain-* → core only. core never imports a chain package and chain packages never import each other; ESLint enforces it rather than leaving it to convention. These packages are vendored copies of the extension's, trimmed of the features below, and are not synced automatically.
| Removed | Why |
|---|---|
| The OpenWallet API | No server to run — the app calls CoinGecko, LI.FI and Jupiter directly over their keyless tiers |
| NFTs (ERC-721/1155, EIP-6551) | Out of scope, and cut from the vendored chain-evm rather than merely hidden |
| Smart accounts (ERC-4337) | Needs a hosted bundler and paymaster — the server dependency this port exists to shed |
| dApp connections (EIP-1193) | An injected provider is a browser mechanism; parity on mobile means WalletConnect |
Prerequisites: Node.js ≥ 20, pnpm 10, and Xcode or Android Studio for the native build.
pnpm install
pnpm build # compile every package — Metro reads dist, not src
pnpm test # vitest across the workspace
pnpm check # format + build + lint + typecheck + testBuild and launch the app. The first run generates the native project and compiles it, which takes a while; later runs take seconds:
pnpm ios
pnpm androidRun these from the repository root rather than from apps/mobile — the root scripts compile the packages first, then hand off to Expo in the right directory. While editing a packages/* file, keep pnpm build:watch running alongside. Expo Go is not enough, since the app links native modules of its own; native projects are generated by expo prebuild and are not checked in.
.npmrcsetsnode-linker=hoisted. Metro resolves modules by walking real parent directories and cannot follow pnpm's symlinked layout.- The vault lives in AsyncStorage, not SecureStore. It is already ciphertext under the user's password, and SecureStore documents values over ~2 KB as unsupported on Android — which a vault holding a seed plus imported keys exceeds.
- Every API key is optional. Settings → API keys takes CoinGecko, LI.FI and Jupiter keys; each provider has a keyless tier that works, and a key only raises the rate limit. A rate-limited price shows up as a missing price rather than an error.
- Swaps are same-chain only, EVM through LI.FI and Solana through Jupiter. Bitcoin has no on-chain DEX to aggregate, so it has no swap screen.
- Testnet mode (Settings) shows only test networks and hides the swap tab; off, only mainnets are listed. Custom networks stay visible either way.
- Public RPCs throttle. Any network's endpoint can be overridden in the app under Networks.
MIT © RedDuck Limited