aifantry
Back to Case Studies

How We Built Cryptozilla: A Non-Custodial Crypto Wallet App in React Native

How we built Cryptozilla — a non-custodial BTC, ETH & ERC-20 wallet in React Native, with BIP-39 HD derivation, AES-256 keystore, biometric auth, certificate pinning, and the FLAG_SECURE + Reanimated security conflict.

Project Duration:4 months
Industry:Web3 & Fintech
Published:May 20, 2026
Tools Used
  • React Native
  • TypeScript
  • BIP-39
  • BIP-44
  • bitcoinjs-lib
  • ethers.js
  • React Native MMKV
  • Redux Toolkit
  • React Navigation 6
  • React Native Reanimated
  • Victory Native
  • CoinGecko API
  • WebSocket
  • Jest
  • Detox
cryptozilla.io

title: "How We Built Cryptozilla: A Non-Custodial Crypto Wallet App in React Native" slug: cryptozilla-non-custodial-crypto-wallet-react-native

We had screenshot prevention working on Android. FLAG_SECURE was set in MainActivity.java, the seed phrase screen was locked down, and the security review was satisfied. Then we ran the app on a Samsung Galaxy S23 Ultra — and the portfolio chart, a gesture-driven Victory Native component animated with React Native Reanimated, started stuttering. On a Xiaomi Mi 13, it was worse: the chart's drag gesture would freeze mid-interaction, then snap. On a Pixel 7, everything ran fine.

We spent two days diagnosing what looked like a Reanimated regression before isolating the real cause: FLAG_SECURE set app-wide on the root window was creating a conflict between Reanimated's UI thread worklets and Android's SurfaceView layer composition on certain OEM Fabric renderer implementations. FLAG_SECURE tells Android to treat the entire window as a secure surface. On Samsung and Xiaomi's custom Android builds, this was interfering with how Reanimated's animated values flush to the native layer.

That was the moment I understood that security requirements don't just add a layer on top of a React Native app. They reach inside and reshape decisions you thought were settled.

Cryptozilla is a non-custodial BTC, ETH, and ERC-20 wallet app — live on iOS and Android — built for a client who wanted to give retail crypto users full self-custody without requiring a hardware wallet. No seed phrase stored on a server. No exchange holding your keys. One app, your keys, your coins.

Who Cryptozilla Are

Cryptozilla is a self-custody mobile wallet targeting retail crypto users who want the security of cold storage with the convenience of a mobile app. The product covers BTC and ETH natively, ERC-20 tokens on Ethereum mainnet, and includes an in-app DEX swap that routes through aggregated liquidity pools. It targets users moving off custodial exchanges — either after the 2022–2023 exchange collapses put the "not your keys, not your coins" argument back on the front page, or because hardware wallets feel too technical for everyday use.

The brief was specific: this had to feel like a consumer fintech product — smooth animations, clean UI, fast price data — while operating with the security architecture of a custody-grade application. That combination is harder than it sounds.

The Problem

Self-custody wallets carry a liability that no custodial product does: a security failure is permanent. A compromised seed phrase or stolen private key doesn't trigger a support ticket. It drains the wallet. There is no recovery.

This forces a different approach to every layer of the architecture. Key material can never touch plaintext in memory longer than necessary. Screenshot prevention isn't optional. Jailbreak detection isn't paranoia. Certificate pinning isn't over-engineering. These are the baseline, not features.

The brief included: BIP-39 mnemonic generation for wallet creation, BIP-44 HD derivation for BTC and ETH addresses from a single seed, AES-256 encrypted keystore using React Native MMKV, biometric authentication — Face ID on iOS, fingerprint on Android — with an encrypted PIN fallback, certificate pinning against the price API and transaction broadcast endpoints, jailbreak and root detection on app launch, and in-app screenshot prevention across all screens displaying key material or balances.

On top of that security foundation: 50+ screens covering wallet creation, seed phrase backup with verification, asset overview, transaction history, send and receive flows with QR scanning, a real-time price dashboard sourcing from CoinGecko with WebSocket updates, a P&L portfolio view with interactive charts, and an in-app DEX swap using aggregated liquidity routing.

That is a full-scale consumer fintech product. On React Native. With a security posture most mobile banking apps don't reach.

What We Built

Cryptozilla ships as a React Native CLI v0.76 app — TypeScript throughout, targeting iOS 15+ and Android 12+. The wallet is HD: one 12-word BIP-39 seed phrase derives all Bitcoin addresses via the BIP-44 path m/44'/0'/0'/0/n and all Ethereum addresses via m/44'/60'/0'/0/n. We used bitcoinjs-lib for BTC address generation and transaction signing, and ethers.js for ETH and ERC-20.

The seed phrase and derived private keys never leave the device in plaintext. They're stored encrypted in React Native MMKV, keyed with AES-256, with the encryption key derived from the user's biometric authentication session. On app launch, biometrics unlock the keystore. PIN provides a fallback. After five failed PIN attempts, the keystore is wiped.

Real-time price data comes from CoinGecko's API with WebSocket-based stream updates for held assets. Price updates flow through Redux Toolkit into a portfolio calculation that recomputes P&L on every tick. Victory Native renders the portfolio performance chart. React Native Reanimated handles the gesture interactions — including the horizontal scrubber on the chart that moves a price tooltip along the line. This scrubber was the component that suffered most from the FLAG_SECURE conflict.

Transaction broadcast runs through Blockstream's Esplora API for BTC and a private Ethereum node endpoint for ETH. Both are certificate-pinned. The DEX swap routes through a liquidity aggregator API, with the confirmation screen showing route, expected output, and slippage before the user signs.

Fifty-plus screens, React Navigation 6, dark and light themes, Redux Toolkit state throughout.

How We Built It

Wallet Architecture & Key Management

The keystore architecture is the most critical piece. We used React Native MMKV with the encrypted storage option — MMKV stores data in memory-mapped files, and with encryption enabled it uses the platform's hardware-backed keystore (Android Keystore on Android, iOS Secure Enclave on iOS) to protect the encryption key. The wallet's private key material is AES-256 encrypted at rest. The encryption key is hardware-bound and only accessible after biometric or PIN authentication.

For wallet generation: entropy is sourced from crypto.getRandomValues(), passed through the BIP-39 wordlist algorithm to produce a 12-word mnemonic, then HD key derivation via BIP-44 produces the master extended private key. Private keys for individual addresses are derived on demand and never stored individually — only the master seed is persisted.

bitcoinjs-lib handles BTC key derivation, P2WPKH address encoding (native SegWit), and PSBT-based transaction construction. ethers.js handles ETH key derivation, checksum address encoding, EIP-1559 transaction construction, and ERC-20 transfer() ABI encoding.

Security Hardening Layer

The security layer runs before the first screen renders. On app launch, we check for jailbreak (iOS) and root (Android) using a native module that inspects for the presence of Cydia, su binaries, and /proc/1/maps anomalies. A detected jailbreak triggers an alert and locks the app.

Certificate pinning is applied to the CoinGecko endpoints and the transaction broadcast endpoints via a native module wrapping NSURLSession (iOS) and OkHttp (Android) with pinned leaf certificates — blocking MITM attacks against the price feed or transaction pipeline.

Biometric authentication uses react-native-biometrics with a challenge-response flow: the app generates a random 32-byte challenge on each session, biometrics sign it using a hardware-backed RSA key (Secure Enclave on iOS, Android Keystore on Android), and the signed result unlocks the MMKV keystore. Biometrics don't just authenticate the user — they cryptographically bind the session to the device's secure hardware.

Screenshot prevention required platform-specific approaches and caused the most unexpected engineering work on the project — covered in full below.

Real-Time Data & Portfolio UI

CoinGecko price data is polled at 30-second intervals via REST and supplemented by a WebSocket price stream for held assets. Updates flow into Redux Toolkit, triggering P&L recalculation on every tick across the portfolio and asset list views.

Victory Native renders the portfolio performance chart — a time-series line showing asset value over 24h, 7d, and 30d windows. React Native Reanimated handles the chart's gesture interactions. The complete navigation structure — 50+ screens — runs on React Navigation 6 with a combination of native stacks and bottom tab navigation.

What Made It Hard — Challenges & Decisions

1. FLAG_SECURE Broke Reanimated on Samsung and Xiaomi

FLAG_SECURE is the standard Android mechanism for blocking screenshots and screen recordings. Set it in MainActivity.java and the entire app window is marked secure. One line.

The problem is what it does to the rendering pipeline on OEM Android builds. On stock Android — Pixel devices — FLAG_SECURE and Reanimated's Fabric interop coexist without issue. On Samsung One UI and Xiaomi MIUI, which both ship customised Fabric renderer implementations, FLAG_SECURE applied to the root window conflicts with how Reanimated flushes animated values from the worklet thread to the native UI thread. On those devices, animated views using useAnimatedStyle drop frames, and gesture handlers using useAnimatedScrollHandler freeze mid-interaction.

The fix: we removed FLAG_SECURE from MainActivity.java entirely and built a per-screen native module. Sensitive screens — seed phrase display, private key export, balance overview, transaction confirmation — call NativeScreenSecurity.enable() on mount and NativeScreenSecurity.disable() on unmount. Non-sensitive screens carry no flag. Reanimated gestures on those screens run without interference. On Samsung and Xiaomi, this eliminated the rendering artifacts entirely.

The lesson is that FLAG_SECURE applied app-wide is a convenience, not a requirement. Selective, per-screen application achieves the same security outcome without the OEM rendering conflicts.

2. iOS Has No FLAG_SECURE Equivalent

On iOS, there is no API that programmatically blocks screenshots. The standard workaround — used by several major European neobanks — is the UITextField secure overlay trick: a transparent UITextField with secureTextEntry = YES placed as a subview of the window causes iOS to suppress screenshots for that window natively.

It works. But it has a side effect: any React Native view rendered above the UITextField in the view hierarchy can have its gesture recogniser captured by the UITextField on subsequent touches. In our case, the Victory Native chart's drag scrubber — a horizontal pan handled by Reanimated — would fire correctly on first touch on the seed phrase and balance screens, then the UITextField gesture recogniser would capture subsequent touches.

The decision: we disabled the drag scrubber entirely on iOS screens where the screenshot overlay is active. Those screens fall back to a tap-to-show-price tooltip instead. The chart renders, the performance line is visible, but no interactive scrubber. We shipped it that way. The security requirement took priority.

This is a known, documented tradeoff in the project notes. The roadmap includes a native-module approach using UIScreen.capturedDidChangeNotification — which detects screenshots and reacts rather than preventing them — as an alternative that doesn't affect the view hierarchy.

3. The Seed Phrase Backup Flow Had to Feel Like UX, Not a Test

BIP-39 backup is a solved problem technically: show the user 12 words, they write them down. The product challenge is that users don't take it seriously until they lose funds. The brief was explicit: the backup flow had to force genuine engagement without being so punishing that users abandoned it.

We built a three-stage flow. First, the 12-word display with a "tap to reveal" interaction — FLAG_SECURE active, screenshot blocked. Second, a shuffle-and-select verification quiz: the user must tap the 12 words in correct order from a grid of 18 options (12 correct + 6 distractors drawn from adjacent BIP-39 wordlist positions — close enough to be plausible, distinct enough to be identifiable if you actually wrote down your seed). Third, a confirmation screen that unlocks the wallet only after the quiz passes. Failed attempts reset the grid without penalty, but you cannot proceed without completing it correctly.

The seed phrase backup flow has a completion rate above 90% in production. The client's primary goal for onboarding was that users who start the backup actually finish it. That number was the outcome they measured.

4. The Biometric Fallback Chain Under Jailbreak

If jailbreak is detected at launch, the app blocks. But what about a device jailbroken after the app is installed, after the keystore is established? Or a device where biometrics fail persistently?

The fallback chain: Face ID / fingerprint → encrypted PIN → lockout after five attempts → keystore wipe. On biometric failure, the app prompts for PIN immediately. On five failed PIN attempts, MMKV keystore is wiped. The user re-imports their seed phrase to recover.

Jailbreak detection runs on every app resume, not just launch. If a jailbreak is detected mid-session, the app clears in-memory keystore state and returns to the lock screen. The persisted MMKV encrypted store is not wiped on jailbreak detection alone — that risks data loss on false positives — but the session is invalidated and full re-authentication is required.

What Changed — The Outcome

Cryptozilla shipped to the App Store and Google Play. Users who previously held BTC and ETH on custodial exchanges can now self-custody from a mobile app that passes standard financial app security testing: no plaintext key material in transit or at rest, screenshot prevention on all sensitive screens, certificate pinning against price and transaction APIs, biometric authentication with hardware-backed key management.

The P&L dashboard with real-time price feeds — sub-second updates on held assets, interactive portfolio chart, token swap with live slippage preview — delivered the consumer fintech feel the client needed. The result is an app that sits closer to Coinbase or Trust Wallet in experience, while operating closer to a hardware wallet in security architecture.

The seed phrase verification quiz achieves a 90%+ completion rate in onboarding. That was the metric the client cared most about: users who start the backup actually finish it.

What's Next — The Roadmap

The client has scoped a second phase covering:

  • Multi-chain expansion: Solana (SOL) and Polygon (MATIC) address derivation and balance tracking
  • WalletConnect v2 integration — enabling Cryptozilla as a signing wallet for DeFi dApp connections
  • Hardware wallet bridge — Ledger SDK integration to co-sign transactions with a paired Ledger device
  • Staking UI — ETH 2.0 staking and liquid staking protocol integrations (Lido, Rocket Pool)
  • iOS screenshot detection via UIScreen.capturedDidChangeNotification to restore the portfolio chart scrubber on sensitive screens without the UITextField overlay side effects

Common Questions About Non-Custodial Wallet Development

What is the difference between a custodial and non-custodial crypto wallet?

A custodial wallet means a third party — an exchange or wallet provider — holds your private keys. They control the funds. A non-custodial wallet means your private keys are generated and stored on your device only. No third party has access. The tradeoff: you're entirely responsible for your seed phrase backup. Lose it, and no one can recover your funds.

How secure is React Native for crypto wallet development?

Secure enough for production, with the right architecture. The requirements: key material stored only in hardware-backed encrypted storage (iOS Secure Enclave, Android Keystore), biometric auth tied to a hardware-bound challenge-response, screenshot prevention on sensitive screens, jailbreak/root detection, and certificate pinning. React Native's JavaScript bridge introduces a surface area that native development doesn't have — which makes these hardening steps non-optional rather than best-practice.

What is BIP-39 and how does it protect a wallet?

BIP-39 is the Bitcoin Improvement Proposal that defines how random entropy is encoded as a human-readable 12 or 24-word mnemonic. Every private key the wallet will ever use is deterministically derived from this seed phrase via BIP-44. The seed phrase is the wallet — which means showing it on screen requires screenshot prevention, and the backup verification flow has to ensure the user has actually recorded it.

Can you actually prevent screenshots in a React Native app?

On Android: yes, via FLAG_SECURE — with the caveat that app-wide application conflicts with React Native Reanimated gesture handlers on certain Samsung and Xiaomi OEM builds. Per-screen activation via a native module is the reliable approach. On iOS: no programmatic screenshot block exists. The UITextField secure overlay suppresses screenshots at the OS level but affects gesture recogniser priority on Reanimated-animated screens. The alternative — detecting screenshots via UIScreen.capturedDidChangeNotification and responding — avoids the gesture conflict but cannot prevent the screenshot itself.

Cryptozilla illustrates a principle that applies across any financial app on React Native: security requirements and UI architecture have to be designed together. The FLAG_SECURE conflict that surfaced in OEM testing, the iOS overlay workaround that cost us the chart scrubber, the biometric fallback chain that had to account for post-install jailbreaks — none of these were visible in the security brief. They emerged from the intersection of the security requirements and the product decisions made weeks earlier.

If the security layer feels like a separate workstream from the UI build, that is the first sign it will cause problems during testing.

We've applied this approach across financial products — from fraud detection and security architecture in fintech AI to regulatory compliance in financial applications. If you're evaluating how to architect a secure mobile product on React Native, our AI integration and automation service covers the security architecture engagement from brief to production.

View all case studies — or get in touch.

Written By

Abrar

Founder, AIfantry | AI Engineer

Ready to achieve similar results?

Our team of AI experts is ready to help you transform your business with tailored AI solutions.

Contact Us Today