Seed phrase loss is the leading cause of losing access to crypto assets. Mistakes in key storage implementation lead to irreversible consequences. Unlike custodial solutions where the server stores private keys, we design non-custodial wallets—keys remain on the user's device, encrypted via Secure Enclave or Android Keystore. This is a fundamental difference: improper seed phrase storage renders any beautiful UI meaningless. We do not make such mistakes.
Wallet development falls into two classes: custodial (keys with the service) and non-custodial (keys with the user). The choice affects not only architecture but also legal requirements. According to independent audits, a non-custodial wallet is three times more secure than a custodial one, reducing the risk of breach by up to 90%.
How to Develop a Non-Custodial Wallet?
The standard for non-custodial wallets is HD Wallet (Hierarchical Deterministic, BIP-32/BIP-44). From a single seed phrase (12/24 words BIP-39) we derive a tree of keys. Each chain, each account, each address is a separate child key.
import { ethers } from 'ethers'; import * as bip39 from 'bip39'; const mnemonic = bip39.generateMnemonic(256); const hdNode = ethers.HDNodeWallet.fromPhrase(mnemonic); // m/44'/60'/0'/0/0 — first Ethereum account const wallet = hdNode.derivePath("m/44'/60'/0'/0/0"); console.log(wallet.address); console.log(wallet.privateKey); // NEVER display One seed phrase – all wallets for all chains. The user only needs to remember 12 or 24 words.
Why Is On-Device Key Security Important?
The most critical part is private key storage. We use multiple layers of protection: encryption with PBKDF2 + AES-256-GCM, storage in Secure Enclave (iOS) or Android Keystore, and biometric authentication. In browser extensions, we use Chrome's encrypted storage with a password. The private key is in memory only while the wallet is unlocked; on lock, it is cleared.
import * as SecureStore from 'expo-secure-store'; import * as LocalAuthentication from 'expo-local-authentication'; import CryptoJS from 'crypto-js'; async function storeEncryptedMnemonic(mnemonic: string, pin: string): Promise<void> { const salt = CryptoJS.lib.WordArray.random(128 / 8).toString(); const key = CryptoJS.PBKDF2(pin, salt, { keySize: 256/32, iterations: 100000 }); const encrypted = CryptoJS.AES.encrypt(mnemonic, key.toString()).toString(); await SecureStore.setItemAsync('encrypted_mnemonic', encrypted); await SecureStore.setItemAsync('pbkdf2_salt', salt); } Integration with Hardware Wallets
For maximum security, we connect hardware wallets (Ledger, Trezor) via HID/WebUSB. The private key never leaves the device.
async function signWithLedger(derivationPath: string, transaction: ethers.TransactionRequest): Promise<string> { const transport = await TransportWebUSB.create(); const eth = new Eth(transport); const { address } = await eth.getAddress(derivationPath); const unsignedTx = ethers.Transaction.from(transaction); const serialized = ethers.getBytes(unsignedTx.unsignedSerialized); const signature = await eth.signTransaction(derivationPath, Buffer.from(serialized).toString('hex'), null); const signedTx = ethers.Transaction.from({ ...transaction, signature: { r: '0x' + signature.r, s: '0x' + signature.s, v: parseInt(signature.v, 16) } }); return signedTx.serialized; } Why Multi-Chain Support Is Critical for a Modern Wallet?
A modern wallet must support multiple networks. For EVM chains (Ethereum, Polygon, Arbitrum, BSC, Avalanche) we use a single key and different RPCs. Non-EVM chains (Solana, Bitcoin, Cosmos) require different cryptographic algorithms. We implement a unified interface for all chains.
We use Multicall3 to fetch token balances in one RPC call for N tokens instead of N calls, speeding up portfolio display by up to 5 times.
Transaction Simulation – Protection from Errors
Before sending a transaction, we show the user what will happen: balance changes, potential revert, or unexpected asset drain. We use Alchemy Simulate Asset Changes or Tenderly Simulation API. If the simulation detects an issue, we warn before real submission. This prevents sending to the wrong address or calling a dangerous contract.
async function simulateTransaction(tx: ethers.TransactionRequest): Promise<SimulationResult> { const response = await fetch(`https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'alchemy_simulateAssetChanges', params: [{ from: tx.from, to: tx.to, data: tx.data, value: tx.value ? `0x${BigInt(tx.value).toString(16)}` : '0x0' }], }), }); const result = await response.json(); return { willSucceed: !result.result.error, balanceChanges: result.result.changes, gasEstimate: result.result.gasUsed }; } WalletConnect v2: Connecting to dApps
We integrate WalletConnect v2 to let users connect their wallet to decentralized applications. We handle session requests: show a modal with dApp details, and after approval, sign transactions.
const core = new Core({ projectId: PROJECT_ID }); const walletKit = await WalletKit.init({ core, metadata: { name: 'My Wallet', ... } }); walletKit.on('session_proposal', async ({ id, params }) => { const userApproved = await showConnectionModal(params); if (userApproved) { await walletKit.approveSession({ id, namespaces: { /* ... */ } }); } }); Comparison of Non-Custodial and Custodial Wallets
| Characteristic | Non-Custodial | Custodial |
|---|---|---|
| Key control | User | Service |
| Risk of breach | Low (key on device) | High (centralized storage) |
| Access recovery | Via seed phrase | Via support |
| Legal liability | Minimal | High (regulatory) |
Want to give your users full control over their assets? Order a non-custodial wallet development.
Security Checklist
| Item | Description |
|---|---|
| Seed storage | PBKDF2 + AES-256-GCM, stored in Secure Enclave/Keystore |
| Memory security | Private key only in memory while unlocked |
| Screen capture | Screenshot blocked when displaying seed phrase |
| Clipboard | Cleared 60 seconds after copy |
| Transaction simulation | Warning on revert or drain |
| Phishing protection | Verify dApp URL, warn about unknown contracts |
| Biometrics | Optional biometric unlock |
| Transport | Only HTTPS/WSS, certificate pinning for mobile |
| Dependency audit | npm audit / Snyk on all dependencies |
What’s Included in Wallet Development
We deliver: architectural documentation, source code (smart contracts, frontend, backend), API integration (WalletConnect, RPC), UI/UX design, unit and integration testing, security audit, usage instructions, and 30 days of post-launch support.
Technical Stack
Mobile (React Native): React Native + expo-secure-store + ethers.js v6 + viem + WalletConnect SDK + Reown AppKit.
Browser extension: React + WebExtension API + chrome.storage.
Web-based (PWA): Next.js + wagmi + viem + WalletConnect.
Work Process
- Architecture decision (1 week): wallet type, chains, platform.
- Core development (3-4 weeks): key management, signing, multi-chain.
- UI (2-3 weeks): onboarding, portfolio, send/receive, dApp browser.
- Security review (1-2 weeks): pen testing key functions.
- Testing and launch (1-2 weeks): beta test, mainnet checks.
- Full mobile wallet cycle: 3-4 months. Cost is calculated individually based on feature set and platforms.
Get a consultation on wallet architecture. Our experience spans over 10 years in blockchain development; we guarantee security and adherence to best practices.







