Feature spec
AdvancedCategory
payments-commerce
Build with AI
1–2 days with FlutterFlow and custom code
Custom build
3–6 weeks custom dev
Running cost
$0–10/mo (100 users) · $50–120/mo (1,000 users)
Works on
Everything it takes to ship a Crypto Wallet — parts, prompts, and real costs.
A crypto wallet needs six components: a QR receive address, a Web3 RPC client (web3dart or ethers.dart), flutter_secure_storage for the private key, a transaction builder that signs client-side, a transaction history fetched from Alchemy or Moralis, and a biometric gate before every send. With FlutterFlow and custom code blocks you can ship a read-only wallet display in 1–2 days; a full send/receive wallet with proper key management is a 3–6 week custom build. Running costs are $0–120/mo depending on RPC call volume.
What a Crypto Wallet Feature Actually Is
A crypto wallet is not a place that stores coins — it stores the cryptographic keys that prove ownership of coins recorded on a public blockchain. Your app's wallet feature has two jobs: display what the user owns (balance, transaction history, prices in USD) and authorize transfers by signing transactions with the private key. The signing happens entirely on the device — the private key never travels to your server or any third party. This is what makes crypto wallet development fundamentally different from building a payment form: the security model, key management, and recovery flows require careful decisions that AI tools can scaffold but rarely get right without specific guidance.
What users consider table stakes in 2026
- QR code displayed for the receive address with a one-tap copy button and haptic feedback — standard for every wallet in 2026
- Real-time balance shown in both the native token and USD equivalent, with a last-updated timestamp
- Send flow that validates the destination address (EIP-55 checksum for EVM chains) before allowing the user to enter an amount
- Gas fee estimate displayed in both gwei and USD equivalent before the user confirms a send — no surprises after signing
- Biometric authentication (Face ID or fingerprint) required before the private key is accessed for any send or sign action
- Transaction history with pending/confirmed status, amounts, and timestamps — with a shimmer loading state on first load
Anatomy of the Feature
Six components. The key management layer and biometric gate are where security fails — and where AI tools take the most shortcuts. Read each component before prompting.
Wallet address display and QR
UIThe qr_flutter Flutter package renders the receive address as a QR code. The address is displayed as copyable text below. The uni_links package handles wallet:// deep links if other apps need to send to your address.
Note: QR code generation is entirely client-side and free. Deep link handling requires URL scheme registration in Info.plist (iOS) and AndroidManifest.xml.
Web3 RPC client
BackendThe web3dart Flutter package queries an Ethereum-compatible JSON-RPC endpoint (Alchemy or Infura) to fetch the wallet balance via eth_getBalance and to broadcast signed transactions via eth_sendRawTransaction. All RPC calls are outbound HTTP from the device.
Note: Each EVM chain has a different RPC endpoint — Ethereum mainnet, Polygon, Base, and BSC are not interchangeable. Prompt for the specific chain before generating code.
Key management layer
Backendflutter_secure_storage stores the encrypted private key or seed phrase in the device's secure enclave (iOS Keychain, Android Keystore). The key is never transmitted to any server. The local_auth package enforces Face ID or fingerprint authentication before the key is read from storage.
Note: SharedPreferences is plaintext on Android — AI tools default to it. Explicitly require flutter_secure_storage in your prompt. Test by inspecting the Android file system to confirm the key is not readable.
Transaction builder
BackendConstructs and signs raw transactions entirely client-side using web3dart. Gas estimation uses an eth_estimateGas RPC call with the transaction parameters. The gas price uses eth_gasPrice or eth_maxFeePerGas for EIP-1559 chains. The signed transaction hex is then broadcast via eth_sendRawTransaction.
Note: Gas estimates go stale within 30 seconds on congested networks. Re-fetch gas price immediately before signing, not when the send form opens.
Transaction history
DataAlchemy's Enhanced APIs or the Moralis Transactions API fetches historical transactions by wallet address. Results are cached locally in SQLite via the sqflite Flutter package for offline display, keyed by transaction hash with block_number, from, to, amount_wei, status, and timestamp.
Note: Alchemy and Moralis both have free tiers that cover 100-user scale. Cache aggressively — transaction history rarely changes for confirmed blocks.
Biometric gate
UIThe local_auth package calls authenticateWithBiometrics() before any operation that reads the private key — initiating a send, signing a message, or displaying the seed phrase. If biometrics are unavailable, fall back to device PIN via LocalAuthentication.authenticate() with deviceCredential fallback.
Note: The biometric check must happen immediately before the key read in the same code path — not on app launch. A user could pass biometrics on launch but initiate a send hours later.
The data model
Wallet metadata lives in Supabase — the public address and label only, never the private key. The private key and transaction cache stay on-device. Run this in the Supabase SQL editor:
1create table public.wallets (2 id uuid primary key default gen_random_uuid(),3 user_id uuid references auth.users(id) on delete cascade not null,4 address text not null,5 label text not null default 'My Wallet',6 chain text not null default 'ethereum',7 created_at timestamptz not null default now()8);910alter table public.wallets enable row level security;1112create policy "Users can view own wallets"13 on public.wallets for select14 using (auth.uid() = user_id);1516create policy "Users can insert own wallets"17 on public.wallets for insert18 with check (auth.uid() = user_id);1920create policy "Users can update own wallets"21 on public.wallets for update22 using (auth.uid() = user_id);2324create policy "Users can delete own wallets"25 on public.wallets for delete26 using (auth.uid() = user_id);2728create index wallets_user_id_idx29 on public.wallets (user_id);Heads up: Private keys are NEVER stored in Supabase or any server — only the public wallet address and a user-facing label. Transaction history is cached in the device's SQLite database (sqflite) with hash, from_address, to_address, amount_wei, status (pending/confirmed/failed), block_number, and timestamp columns.
Build it — pick your path
Each tool fits this feature differently. Switch tabs to compare fit, time, the exact steps, and a copy-paste prompt.
The right path for a production crypto wallet. Full control over BIP39/BIP44 key derivation, multi-chain support, hardware wallet integration, MPC key splitting for institutional custody, and regulatory compliance flows.
Step by step
- 1BIP39 mnemonic generation and BIP44 HD key derivation using the bip39 and ed25519_hd_key Dart packages for proper multi-account wallet support
- 2Multi-chain architecture: separate RPC endpoints and gas token handling for Ethereum, Polygon, Base, Optimism, and BSC
- 3MPC (multi-party computation) key splitting for enterprise custody where no single device holds the complete private key
- 4KYC/AML integration (Sumsub or Jumio) for fiat on-ramp regulatory requirements
Where this path bites
- Minimum 3–6 weeks for a production-grade implementation; security audit strongly recommended before launch and adds 2–4 weeks
- Deep expertise in both mobile security and Web3 cryptography is required — this is not a standard app build
Third-party services you'll need
Four services power the wallet's data layer. All have free tiers that cover early-stage usage; RPC call volume is what drives costs at scale.
| Service | What it does | Free tier | Paid from |
|---|---|---|---|
| Alchemy | Primary Ethereum/EVM JSON-RPC provider — eth_getBalance, eth_sendRawTransaction, Enhanced APIs for transaction history | Free (300M compute units/mo) | Growth $49/mo (1.5B CUs) (approx) |
| Infura | Backup RPC provider; use as a fallback if Alchemy is rate-limited or has an outage | Free (100K requests/day) | $50/mo (3M requests/day) (approx) |
| Moralis | Multi-chain transaction history and token prices — simpler than building raw RPC history queries | Free (40K requests/mo) | $49/mo (100K requests/mo) (approx) |
| CoinGecko API | Token prices for USD conversion — converts raw ETH and ERC-20 balances to fiat display values | Free (30 calls/min) | $129/mo unlimited (approx) |
Swipe the table sideways to see pricing.
What it costs to run
Drag through the tiers to see how your monthly bill scales with users — no surprises later.
Estimated monthly running cost
Alchemy free tier covers light usage at 100 users; CoinGecko free tier; Supabase free tier for wallet metadata storage. Near-zero fixed cost.
Estimates use verified 2026 pricing and assume typical usage per user. Your real bill depends on activity, storage, and third-party plans.
What breaks when AI tools build this
The failures people actually hit on their first build — the symptom, why it happens, and the exact fix.
Private key stored in SharedPreferences — plaintext on Android
Symptom: Flutter's SharedPreferences writes to a plain XML file that is readable without root access on Android debug builds and potentially accessible on rooted devices. AI tools default to SharedPreferences for any persistent data because it requires no additional packages. On iOS, NSUserDefaults has similar plaintext storage. A user's entire wallet is compromised if the device is rooted or if the app backup is not encrypted.
Fix: Explicitly require flutter_secure_storage in your prompt — it uses Android Keystore and iOS Secure Enclave, which store keys in hardware-backed secure memory. Verify the key is protected: on Android, use the adb shell and confirm the key does not appear in the shared_prefs directory. Never use SharedPreferences, Hive without encryption, or sqflite for private key storage.
Gas fee estimate is stale at transaction confirmation time
Symptom: AI-generated wallet code typically fetches the gas estimate when the send form opens — sometimes minutes before the user taps Confirm. On busy networks like Ethereum mainnet, gas prices can move 2–5x in seconds. A transaction submitted with a stale gas price is either rejected (too low) or overpays significantly (if the estimate was too high).
Fix: Re-fetch eth_estimateGas and eth_gasPrice (or eth_maxFeePerGas for EIP-1559) immediately before calling signTransaction() — not when the send form renders. Display a 'Gas refreshed' timestamp next to the estimate and show a warning icon if it is more than 30 seconds old. Disable the Confirm button while the refresh is in progress.
RPC endpoint hardcoded to Ethereum mainnet during development
Symptom: AI generates RPC URLs for Ethereum mainnet by default. During development, any accidental test send uses real ETH. A developer testing a send flow with 0.001 ETH can lose $3–5 per test transaction. Even a single accidental mainnet send during development causes real financial loss.
Fix: Scaffold a NETWORK environment variable from day one. Default to Sepolia testnet (https://eth-sepolia.g.alchemy.com) in development. Display a red 'TESTNET' banner in your wallet UI when not on mainnet. Require an explicit MAINNET=true flag in the production build configuration before switching the RPC endpoint.
Transaction history shows empty on first load — API 429 silently swallowed
Symptom: Alchemy and Moralis impose rate limits per API key. When the app launches, all active users may simultaneously request transaction history, hitting the rate limit. FlutterFlow custom code blocks that do not handle HTTP 429 responses show an empty list with no error message — users think their history is gone.
Fix: Add retry logic with exponential backoff in your Custom Action: on 429, wait 1 second, retry; on second 429, wait 3 seconds, then show an error state. Display a shimmer loading skeleton while history loads. Cache the last successful response in sqflite so users see recent history instantly on subsequent opens, even before the API responds.
Seed phrase displayed permanently on screen without obscuring
Symptom: When AI generates the seed phrase backup screen, it typically renders all 12 or 24 words in a visible grid that stays on screen indefinitely. This is a critical security failure: anyone who glances at the screen — or captures a screenshot — has full wallet access forever.
Fix: Require a tap-to-reveal pattern: show the seed phrase blurred by default with a 'Tap to reveal — be sure no one is watching' prompt. Implement screen capture prevention using flutter_windowmanager's FLAG_SECURE on Android and ignoresScreenCapture on iOS. Auto-hide the words after 60 seconds of display. Log seed phrase reveal events (without the words themselves) for security auditing.
Best practices
Store private keys exclusively in flutter_secure_storage — never SharedPreferences, Hive without encryption, or any cloud storage including Supabase
Require biometric authentication immediately before the key read in the same code path, not once on app launch
Default to testnet (Sepolia for Ethereum) in development and display a prominent banner — one accidental mainnet send costs real money
Re-fetch gas price immediately before signing a transaction, not when the send form opens; gas moves fast on congested networks
Validate destination addresses with EIP-55 checksum validation before allowing any amount entry — wrong-address sends are irreversible
Cache transaction history in sqflite and token prices in memory (1-minute TTL) to reduce RPC call volume and keep the UI responsive offline
Implement screen capture prevention (FLAG_SECURE / ignoresScreenCapture) on any screen that shows the seed phrase or private key
Show a 'Pending' status immediately after broadcasting a transaction — poll eth_getTransactionReceipt every 5 seconds and update status when block_number is set
When You Need Custom Development
FlutterFlow with custom code blocks covers read-only display and simple single-chain wallets. A production wallet for real users requires custom development:
- Multi-chain support across 5 or more networks with automatic chain detection — each chain has different RPC endpoints, gas tokens, and address formats that cannot be handled by a simple environment variable swap
- MPC (multi-party computation) or hardware wallet integration for enterprise custody, where no single device holds the complete private key
- In-app DEX swap functionality requiring integration with Uniswap or 1inch smart contracts and liquidity routing logic
- KYC/AML compliance requirement for a fiat on-ramp — regulatory verification flows (Sumsub, Jumio) need custom integration and server-side identity record keeping
RapidDev builds these features for production
Real auth, database, and payments — fixed price, delivered in weeks. You own 100% of the code.
Frequently asked questions
Is it safe to store private keys in a mobile app?
Yes, if you use the device's secure enclave — iOS Keychain or Android Keystore — via flutter_secure_storage. These hardware-backed stores are resistant to extraction even on rooted devices. What is not safe is storing keys in SharedPreferences, plain files, or any cloud database. The key must never leave the device; your server should only ever see the public wallet address.
What blockchain should I build on — Ethereum, Polygon, or Base?
For most consumer apps: Base (an Ethereum Layer 2 built by Coinbase). Gas fees on Base are under $0.01 per transaction, compared to $1–20 on Ethereum mainnet. Base is EVM-compatible, so all Ethereum tooling (web3dart, Alchemy, Metamask) works without changes. Polygon is a solid alternative. Build on Ethereum mainnet only if your use case specifically requires it — gas costs will frustrate your users.
Can users recover their wallet if they lose their phone?
Only if you implement a seed phrase backup flow. A BIP39 12-word or 24-word mnemonic can regenerate the private key on any device. Your app must walk users through writing it down before their first transaction, store only the mnemonic in flutter_secure_storage (never cloud sync), and provide an import flow on reinstall. Without this, a lost phone means lost funds — permanently and irreversibly.
How do I show token prices in USD?
Fetch the token's price in USD from the CoinGecko API using the token's contract address or CoinGecko coin ID. Cache the price in memory with a 1-minute TTL — calling CoinGecko on every balance display will exhaust the free tier quickly. Multiply the raw balance (converted from wei using web3dart's EtherAmount) by the USD price to get the fiat value.
Do I need a license to build a crypto wallet?
For a non-custodial wallet (where users hold their own keys), most jurisdictions do not currently require a license — your app is a key management tool, not a financial intermediary. If you add fiat on/off ramps, custody services, or exchange functionality, money transmission or VASP regulations apply in most countries. Consult a lawyer familiar with crypto regulation in your target markets before launch.
What's the difference between custodial and non-custodial wallets?
A custodial wallet means your company holds the private keys on behalf of users — like an exchange. You are legally responsible for the funds and face significant regulatory requirements. A non-custodial wallet (what this guide covers) means the user's device holds the private key; you never have access to their funds. Non-custodial is simpler to build legally, but puts full responsibility for key backup on the user.
How do gas fees work and can I pay them for users?
Gas fees are paid to the blockchain network validators for processing transactions — they go to miners/validators, not to you. They fluctuate with network congestion. You can sponsor gas fees for users via account abstraction (EIP-4337) using a paymaster service like Pimlico or Alchemy's Gas Manager — your app's account covers the gas and charges users separately or absorbs the cost. This significantly improves UX but adds architectural complexity.
Can I add NFT display to the wallet?
Yes — the Alchemy NFT API (getNFTsForOwner) and Moralis NFT API return all NFTs owned by an address including metadata, image URLs, and collection names. Display them in a grid with lazy-loading images. NFT display is read-only and free at small scale; it adds no signing or transaction complexity. Prompt for it as a separate screen tab within the same wallet project.
Need this feature production-ready?
RapidDev builds a crypto wallet into real apps — auth, database, payments — at $13K–$25K.
Book a free consultation30-min call. No commitment.