Software for Android-based POS machines, providing a simple, secure, and private way for small merchants to charge and receive Bitcoin payments across all layers (on-chain, Lightning, etc).
Small merchants often face barriers to accepting Bitcoin as a payment method. POS SatStack aims to remove those barriers by offering a solution that runs directly on the Android POS machines already used day-to-day, with no additional hardware required.
The app is built with Kotlin + Jetpack Compose and follows a layered architecture:
- UI layer — Compose screens, Material3, follows the system theme (light/dark)
- Wallet abstraction layer — three neutral contracts (
OnChainWalletEngine,Signer,ChainDataSource) shield every ViewModel, Service, and screen from the concrete Bitcoin library. Swapping the chain backend (Esplora ↔ Kyoto ↔ Floresta) or the wallet engine is localised to a single@Bindschange inWalletModuleplus a Gradle dep flip. - Bitcoin implementation — powered by BDK Android 2.x (BDK 1.x Rust core), using BIP-84 native SegWit descriptors.
Six library-agnostic contracts sit between the UI and BDK: OnChainWalletEngine, Signer, ChainDataSource, WalletStorage, SignerSecretStore, and BiometricAuthenticator. Swapping the chain backend (Esplora ↔ Kyoto ↔ Floresta) or adding a hardware signer (TAPSIGNER, airgap QR) is localised to a single @Binds change plus a Gradle dep flip.
See docs/architecture.md for the wiring diagrams (contract ↔ implementation, send flow, chain-backend swap) and the step-by-step swap procedure.
wallet/
WalletNetwork.kt — network enum
WalletDescriptor.kt — external/internal descriptor + network
BitcoinAddress.kt — value class for Bitcoin addresses
WalletTransaction.kt — lightweight tx model for the UI
SyncProgress.kt — sync state (Idle, FullScan, Syncing)
OnChainWalletEngine.kt — main contract
WalletBackup.kt — sealed backup payloads (Bip39, Xpub, SCB, …)
WalletError.kt — neutral error hierarchy
FeePolicy.kt / PsbtTypes.kt — fee + PSBT value types
bitcoin/
BdkOnChainEngine.kt — BDK 2.x implementation
BdkErrors.kt — org.bitcoindevkit.* → WalletError
chain/
ChainDataSource.kt — interface
EsploraChainDataSource.kt — active backend (via BDK EsploraClient)
signer/
Signer.kt / SigningContext.kt — signer contract
BdkSeedSigner.kt — software seed + biometric
SignerSecretStore.kt — mnemonic contract
AndroidKeystoreSignerSecretStore.kt— StrongBox + auth-required
MnemonicCipher.kt — dedicated Keystore AES-GCM key
BiometricAuthenticator.kt — prompt contract
AndroidBiometricAuthenticator.kt — BiometricPrompt impl
ActivityHolder.kt — weak ref to the resumed FragmentActivity
storage/
WalletStorage.kt — descriptor persistence contract
SecureWalletStorage.kt — EncryptedSharedPreferences impl
- Mnemonic lives in a dedicated
mnemonic_secure_prefsfile, double-wrapped: the prefs file itself is encrypted by a non-auth-requiredMasterKey, and the mnemonic payload inside is encrypted again by a separate AndroidKeyStore AES-GCM key withsetUserAuthenticationRequired(true)and a 30-second validity window. Reading the mnemonic therefore always requires a recent biometric/PIN prompt viaBiometricPrompt. StrongBox is attempted first, falling back to TEE. - Descriptors live in a separate
wallet_secure_prefsfile, encrypted at rest by a standardMasterKey. Reads do not prompt the user (no need — descriptors are accessed on every boot). - Wallet SQLite cache is stored under
noBackupFilesDir/bdk/so it is excluded from Android cloud backup.noBackupFilesDir/ldk/is reserved for the future Lightning node state. allowBackup="false"plus explicitdata_extraction_rules.xmlblock both secure prefs files from cloud backup and device transfer.- QR codes are generated locally using ZXing — no data leaves the device.
- Create a new BIP-84 (native SegWit) wallet with a 12-word seed phrase
- Import an existing wallet from a 12 or 24-word mnemonic
- View and back up the seed phrase
- Delete the wallet from the device
- Generate new receive addresses
- Display address as a QR code (ZXing) for easy scanning
- Copy address to clipboard
- List all wallet transactions grouped by date
- Unconfirmed (pending) transactions appear in a dedicated section at the top
- Each transaction shows direction (sent/received), amount, txid, and block height
- Tap a transaction to view it on mempool.space (supports signet, testnet, and mainnet)
- Esplora-based synchronization via BDK's built-in
EsploraClient- Signet:
https://mempool.space/signet/api - Mainnet:
https://blockstream.info/api - Testnet:
https://blockstream.info/testnet/api
- Signet:
- Automatic sync on app start via
WalletSyncService - Full scan for first-time or imported wallets; incremental sync thereafter
- Forced full scan when
CHAIN_BACKENDchanges between builds (dev-time swap between Esplora / Kyoto / Floresta) - Global progress indicator visible across all screens
- Balance display (BTC + sats) with manual refresh
- Custom
AppLoggerutility with info, warning, and error levels - Logs are emitted only in debug builds — silent in production
- Android 8.0+ (API 26)
- Android-based POS machine or any Android device for development/testing
| Layer | Technology |
|---|---|
| Language | Kotlin |
| UI | Jetpack Compose + Material3 |
| Navigation | Navigation Compose 2.8 (type-safe routes) |
| DI | Hilt 2.54 + KSP |
| Bitcoin wallet | BDK Android 2.3.1 |
| Chain backend | Esplora (via BDK EsploraClient); Kyoto / Floresta reserved as swap targets |
| Secure storage | EncryptedSharedPreferences (security-crypto 1.1.0-alpha06) |
| Biometric | androidx.biometric 1.2.0-alpha05 + AndroidKeyStore auth-required key |
| QR code | ZXing Core 3.5.3 |
| Serialization | kotlinx-serialization |
- Android app installable on POS machines
- Home screen with Charge and Settings actions
- Wallet abstraction layer with BDK integration
- Create, import, and delete wallet
- Generate receive addresses with QR code
- Electrum sync (full scan + incremental)
- Balance display and transaction history
- Secure storage for seed phrases and descriptors
- NFC integration for charging via SatsCard
- Tap-to-pay flow for a seamless point-of-sale experience
- Pilot with selected merchants
- Feedback collection and iteration on user experience
This project is licensed under the MIT License. See the LICENSE file for details.