Sample Code Architecture and Folder Structure
Introduction
Building a high-quality Bitcoin non-custodial wallet is not just about APIs or cryptography. Good internal architecture is critical for:
Security
Maintainability
Upgradeability (e.g., Taproot, Silent Payments, Lightning)
Scaling to production
This section outlines a professional code architecture recommended for Bitcoin non-custodial wallets.
It is designed for mobile apps (iOS/Android), web apps, or lightweight desktop clients.
Even small teams should aim for this standard if they want to build trusted wallets.
Core Principles
Modular by Responsibility: Separate concerns clearly: key management, address derivation, transaction building, blockchain interface, UI.
Stateless Critical Functions: Critical cryptographic operations (key generation, signing) should be stateless, pure functions without side effects.
Encryption at Rest: Private keys, seeds, and any critical metadata must be encrypted at rest before storage.
Abstract Blockchain Interfaces: Wallet logic should not hard-depend on Bitnob or any single blockchain provider. Use abstraction layers for blockchain interactions.
Pluggable Fee and Policy Engines: Fee estimators, coin selection strategies, and transaction building policies should be modular and replaceable.
Recommended Folder Structure
Core Modules Explained
/core/keys/: Handles generation, encryption, decryption, restoration of private keys and seeds. Should use strong cryptographic libraries (libsodium, bitcoinjs-lib, BDK bindings).
/core/addresses/: Handles address derivation from HD wallets. Supports all address types: Legacy, SegWit, Taproot.
/core/transactions/: Builds, signs, and prepares Bitcoin transactions. Handles fee estimation and change output generation.
/core/utxos/: Manages UTXO selection, consolidation, and optimization strategies.
/infrastructure/bitnob/: Handles all external blockchain interactions using Bitnob RPC. Isolate these so that in future you can swap providers if needed without deep refactor.
/models/: Data models representing wallet objects (wallet, address, UTXO, transaction).
/utils/: Helper functions: formatting, fiat conversions, error handling, logging.
/config/: Network settings (mainnet, testnet), fee policies, hardcoded constants.
/screens/: UI Screens (for mobile or web applications). UI logic should call service layers, not handle raw cryptography.
/services/: Specialized services: backup management, alerts, webhook processing, mempool monitoring.
/tests/: Unit tests for core modules, integration tests for workflows, end-to-end tests simulating real user flows.
Important Architectural Recommendations
Keep Key Operations Local: Private key generation, signing, and encryption should only happen on-device. Backend servers and Bitnob should never see or touch keys.
Abstract Blockchain Client: Wrap Bitnob APIs in an internal service class.
E.g., BlockchainClient.watchAddress(),
BlockchainClient.broadcastTransaction().
This allows you to swap Bitnob for direct Bitcoin Core RPC or Esplora APIs later if ever needed.
Batch API Calls When Possible: Batch watch requests, batch balance queries, batch transaction fetches to minimize network overhead.
Fail Gracefully: If Bitnob API fails or internet is down, the wallet app should queue actions (like sending transactions) and retry later.
Secure Webhook Handling: All incoming webhooks from Bitnob should be authenticated (HMAC signatures recommended). Never trust incoming data blindly.
Plan for Multi-Account Wallets: Even if you start with single accounts, design your data models to support multiple accounts (e.g., user wallets, merchant wallets, savings wallets).
Code Readability Guidelines
Naming Clarity:
Use clear, descriptive names. e.g., deriveExternalAddress() not getAddr().
Small, Focused Functions:
Each function should do one thing only (Single Responsibility Principle).
Document Security-Sensitive Functions:
Functions dealing with seeds, private keys, signing must have explicit doc comments explaining what they do and what they don't.
Explicit Error Handling:
Never swallow errors in cryptography or blockchain code. Always surface them cleanly and visibly.
Unit Test Critical Paths:
Key generation, signing, transaction building must have 100% test coverage.
Example Minimal Wallet App Startup Flow (Pseudocode)
Final Best Practices
Build cryptographic flows first.
Build blockchain interface second.
Build UI/UX third.
Always separate critical wallet logic from user interface code.