Rename 9 Pearl Wallet screenshots to prldroid-problem-screenshot-01..09.png and logo.png to prl-logo.png. Add prldroid-problems-screenshots-index.md documenting UI/UX issues (auto-lock cycling, chart anchoring, balance toggle, hardcoded address risk, stub balances, network fee choices, blank blockchain, top-button ambiguity, recovery/network input needs). Add SplashScreen with |
||
|---|---|---|
| .claude-flow/data | ||
| app | ||
| dist | ||
| DOCS | ||
| gradle | ||
| LOGS | ||
| scripts | ||
| signing | ||
| wallet-go | ||
| web-icons | ||
| .env | ||
| .env.example | ||
| .gitignore | ||
| agentdb.rvf | ||
| agentdb.rvf.lock | ||
| build.gradle.kts | ||
| CHECKLIST.md | ||
| gradle.properties | ||
| local.properties.template | ||
| prldroid-icon-square.png | ||
| prldroid-logo.png | ||
| README.md | ||
| ROADMAP.md | ||
| settings.gradle.kts | ||
| version.json | ||
| version.txt | ||
prldroid-wallet
Native Android wallet for the Pearl blockchain.
Built with Jetpack Compose and gomobile — the Go wallet engine from the pearl-ai-mining monorepo runs embedded inside the app as a compiled Android AAR. No reimplementation; the same debugged Go code that powers the Oyster daemon is the wallet internals here.
Architecture
┌─────────────────────────────────────────────────────┐
│ Android App (Kotlin) │
│ │
│ Jetpack Compose UI ──► WalletViewModel │
│ 4 screens │ │
│ Wallet / Activity / ▼ │
│ Transact / Settings WalletRepository │
│ ┌──┴──────────────┐ │
│ │ │ │
│ OysterBridge PearlExplorerApi│
│ (Kotlin) + OtcPricingApi │
│ │ (Retrofit/OkHttp│
│ │ → real URLs) │
└──────────────────────────┼─────────────────────────-┘
│ JNI / gomobile bind
┌──────────────────────────▼──────────────────────────┐
│ oyster.aar (Go → Android AAR) │
│ │
│ androidbridge/bridge.go │
│ ├─ wallet.NewLoader(…) │
│ ├─ loader.CreateNewWallet / OpenExistingWallet │
│ ├─ chain.NewRPCClient(pearld WebSocket) │
│ ├─ w.CalculateBalance(minConfs) │
│ ├─ w.NewAddress(0, waddrmgr.KeyScopeBIP0086) │
│ ├─ w.ListTransactions(skip, count) │
│ └─ w.SendOutputs(…) via PayToAddrScript + Taproot │
│ │
│ Source: wallet-go/ (wallet-go/ (vendored, module mba.robin/prlwallet)) │
└─────────────────────────────────────────────────────┘
Key design decisions
| Choice | Reason |
|---|---|
| gomobile bind | Compiles the existing Go wallet to an Android AAR without any reimplementation. The wallet's BIP-32/44/86 HD key derivation, UTXO selection, and transaction signing run as-is. |
| Jetpack Compose | Native Android UI — no WebView, no React Native overhead. Full control over the glassmorphic dark theme. |
| Explorer fallback | If oyster.aar is absent (e.g. first clone before building the AAR), the app falls back to read-only explorer mode using live data from lordofpearls.xyz. |
| BIP-86 / Taproot | Pearl uses KeyScopeBIP0086 (purpose 86) and KeyScopePQ (purpose 222, post-quantum XMSS). GetNextAddress() derives from the BIP-86 external path, producing prl1… bech32 Taproot addresses. |
| No mocks | All network calls hit real endpoints. PearlExplorerApi → https://lordofpearls.xyz, OtcPricingApi → https://otc.lordofpearls.xyz. A mobile User-Agent header is set so the explorer doesn't block the requests. |
Screens
| Screen | Content |
|---|---|
| Wallet | Balance card (PRL + USD equivalent), 24 h price/change from OTC, sparkline chart, network stats (block height, hash rate, peers, mempool), recent transactions |
| Activity | Full transaction history with search, send/receive filter chips, grouped by date |
| Transact → Send | Amount entry, address input (paste / QR scan), fee estimate, review step, biometric/passphrase auth, success confirmation with txid |
| Transact → Receive | Live QR code generated from your prl1… address, copy + share |
| Settings | Biometric toggle, auto-lock timer, network (mainnet/testnet), developer mode, currency, language, disconnect |
Live data sources
| Source | URL | Used for |
|---|---|---|
| Pearl Explorer | https://lordofpearls.xyz/stats |
Block height, hash rate, difficulty, peer count, mempool |
| Pearl Explorer | https://lordofpearls.xyz/?wallet=ADDRESS&format=json |
Wallet balance, confirmed/unconfirmed, transaction history |
| Pearl OTC | https://otc.lordofpearls.xyz/ |
PRL/USD price, 24 h change, bid/ask order book |
The OTC platform charges 1.8 % on trades and settles PRL ↔ USDC instantly on-ledger.
Prerequisites
For building the Go AAR
| Tool | Version | Notes |
|---|---|---|
| Go | 1.21+ | https://go.dev/dl/ |
| gomobile | latest | installed automatically by build-wallet-aar.sh |
| Android NDK | r25c+ | install via Android Studio → SDK Manager → NDK |
ANDROID_NDK_HOME |
— | must point at the NDK directory |
For the Android app
| Tool | Version |
|---|---|
| Android Studio | Hedgehog+ |
| Android Gradle Plugin | 8.5.2 |
| Kotlin | 2.0.21 |
| Compile SDK | 35 |
| Min SDK | 26 (Android 8.0) |
Build
1 — Clone and get the wallet source
The monorepo is included in wallet-go/. If you cloned without it:
# The monorepo is already in wallet-go/ in this repo.
# If you need to refresh it from your Forgejo:
rsync -a git.robin.mba/rcheung/pearl-ai-mining/ wallet-go/
2 — Build the Go wallet AAR
chmod +x scripts/build-wallet-aar.sh
./scripts/build-wallet-aar.sh
# Writes: app/libs/oyster.aar (~15–25 MB, first build ~3 min)
What the script does:
- Checks
wallet-go/go.modandwallet-go/androidbridge/bridge.goexist - Installs
gomobile/gobindif absent - Runs
gomobile init - Runs
gomobile bind -target=android -androidapi 26 -javapkg mba.robin.prlwalletbinding theandroidbridgepackage from inside the pearl module
The bridge package (wallet-go/androidbridge/bridge.go) imports:
"mba.robin/prlwallet/node/btcutil"
"mba.robin/prlwallet/node/chaincfg"
"mba.robin/prlwallet/node/txscript"
"mba.robin/prlwallet/node/wire"
"mba.robin/prlwallet/wallet/chain"
"mba.robin/prlwallet/wallet/waddrmgr"
"mba.robin/prlwallet/wallet/wallet"
All real packages from the monorepo — nothing stubbed.
3 — Set up signing
Copy the production keystore into signing/ and create signing/keystore.properties:
cp /path/to/production.keystore signing/production.keystore
cp signing/keystore.properties.template signing/keystore.properties
# Edit signing/keystore.properties — fill storePassword, keyAlias, keyPassword
signing/keystore.properties and signing/production.keystore are in .gitignore
and must never be committed.
The HelloWord production keystore (May 2026) uses:
- Key alias:
upload - Keystore type: PKCS12
- SHA-256 fingerprint:
56:C1:36:74:EF:22:DF:95:DE:B1:E5:C4:68:82:0E:8C:FA:3E:A2:F5:22:51:17:49:AB:7B:6E:5B:DE:3B:D9:43
4 — Add fonts
Download and place in app/src/main/res/font/:
| File | Source |
|---|---|
jetbrainsmono_regular.ttf |
https://www.jetbrains.com/lp/mono/ |
jetbrainsmono_medium.ttf |
same |
jetbrainsmono_semibold.ttf |
same |
jetbrainsmono_bold.ttf |
same |
hankengrotesk_regular.ttf |
https://fonts.google.com/specimen/Hanken+Grotesk |
hankengrotesk_medium.ttf |
same |
hankengrotesk_semibold.ttf |
same |
hankengrotesk_bold.ttf |
same |
5 — Open in Android Studio and build
File → Open → /path/to/prldroid-wallet
File → Sync Project with Gradle Files
# Release builds (signed):
Build → Generate Signed Bundle / APK
├── Android App Bundle (.aab) → upload to Play Console
└── APK → direct / sideload distribution
# Debug build:
./gradlew assembleDebug
Play Console deliverables
| Artifact | Location after build | Upload to |
|---|---|---|
.aab (App Bundle) |
app/build/outputs/bundle/release/app-release.aab |
Play Console → Release |
.apk |
app/build/outputs/apk/release/app-release.apk |
direct distribution |
| Native debug symbols | app/build/outputs/native-debug-symbols/release/native-debug-symbols.zip |
Play Console → Release → Add native debug symbols |
Connecting to a Pearl node
The app reads BuildConfig.WALLET_ADDRESS (set in app/build.gradle.kts) as the
default display address. To connect the embedded Oyster wallet to a live pearld node,
call from Kotlin:
// via OysterBridge (injected by Hilt)
oyster.init(context.filesDir.absolutePath + "/oyster", "mainnet")
oyster.connectToPearld(
rpcURL = "yournode.example.com:44107", // mainnet default
rpcUser = "rpcuser",
rpcPass = "rpcpass",
certBytes = /* TLS cert bytes, or null for no-TLS */,
)
oyster.openWallet("your-pub-passphrase")
Default RPC ports (from wallet/config.go):
| Network | pearld RPC | Oyster legacy RPC |
|---|---|---|
| mainnet | 44107 | 44207 |
| testnet | 44109 | 44209 |
| testnet2 | 44111 | 44211 |
| simnet | 18556 | 18554 |
Project structure
prldroid-wallet/
├── app/
│ ├── build.gradle.kts # AGP config, signing, buildConfigFields
│ ├── libs/ # oyster.aar goes here after build
│ └── src/main/
│ ├── AndroidManifest.xml
│ ├── java/xyz/lordofpearls/wallet/
│ │ ├── MainActivity.kt # single-activity entry point
│ │ ├── PearlApplication.kt # @HiltAndroidApp
│ │ ├── data/
│ │ │ ├── api/
│ │ │ │ ├── NetworkModule.kt # Hilt: Retrofit instances
│ │ │ │ ├── PearlExplorerApi.kt # lordofpearls.xyz REST
│ │ │ │ └── OtcPricingApi.kt # otc.lordofpearls.xyz REST
│ │ │ └── repository/
│ │ │ ├── WalletRepository.kt # merges Go wallet + explorer
│ │ │ └── RepositoryModule.kt # Hilt provider
│ │ ├── ui/
│ │ │ ├── components/
│ │ │ │ ├── AppShell.kt # PearlAppBar, PearlBottomNav, PearlGlyph
│ │ │ │ └── Glass.kt # GlassCard, IridescentBackdrop, LabelMono
│ │ │ ├── screens/
│ │ │ │ ├── WalletScreen.kt
│ │ │ │ ├── ActivityScreen.kt
│ │ │ │ ├── TransactScreen.kt
│ │ │ │ └── SettingsScreen.kt
│ │ │ └── theme/
│ │ │ ├── Color.kt # Obsidian Iridescence palette
│ │ │ ├── Type.kt # JetBrains Mono + Hanken Grotesk
│ │ │ └── Theme.kt # MaterialTheme dark scheme
│ │ ├── viewmodel/
│ │ │ └── WalletViewModel.kt
│ │ └── wallet/
│ │ └── OysterBridge.kt # Kotlin wrapper over oyster.aar via reflection
│ └── res/
│ ├── drawable/ic_pearl_splash.xml # vector Pearl glyph
│ ├── values/{colors,strings,themes}.xml
│ └── xml/{backup_rules,data_extraction_rules,network_security_config}.xml
├── wallet-go/ # wallet-go/ (vendored, module mba.robin/prlwallet) (Go source)
│ ├── androidbridge/
│ │ └── bridge.go # gomobile-compatible bridge — imports real wallet pkgs
│ ├── wallet/ # Oyster daemon source
│ │ ├── wallet/wallet.go # wallet.Wallet — CalculateBalance, NewAddress, SendOutputs …
│ │ ├── wallet/loader.go # wallet.Loader — CreateNewWallet, OpenExistingWallet
│ │ ├── chain/btcd.go # chain.NewRPCClient → pearld WebSocket
│ │ ├── waddrmgr/ # HD key manager (BIP-32/44/86/PQ)
│ │ ├── walletdb/ # BoltDB wallet database layer
│ │ ├── wtxmgr/ # wallet transaction manager
│ │ └── rpc/
│ │ ├── walletrpc/api.pb.go # gRPC protobuf bindings
│ │ └── legacyrpc/ # JSON-RPC server (used by desktop)
│ ├── node/ # pearld consensus node (btcd fork)
│ ├── go.mod # module: mba.robin/prlwallet
│ └── go.sum
├── gradle/libs.versions.toml # version catalog
├── scripts/
│ └── build-wallet-aar.sh # gomobile bind wrapper
├── signing/
│ ├── keystore.properties.template # copy → keystore.properties, fill passwords
│ ├── keystore.properties # ← gitignored, fill locally
│ └── production.keystore # ← gitignored, add locally
├── settings.gradle.kts
├── build.gradle.kts
└── .gitignore
Visual design
The UI mirrors the lordofpearls.xyz aesthetic:
| Token | Value | Role |
|---|---|---|
ObsidianDeep |
#060A14 |
App background |
Surface |
#0B1325 |
Card base |
PrimaryContainer |
#67E8F9 |
Cyan accent — balance, active nav, buttons |
Secondary |
#FFC640 |
Amber — warnings, price change |
TertiaryContainer |
#60F191 |
Green — received, success |
ErrorColor |
#FFB4AB |
Red — failed tx, disconnect |
GlassCard composable: translucent dark gradient background + 1 px iridescent
linear-gradient border (cyan → amber → green). Matches the CSS .glass class on
the web explorer exactly.
Security notes
- Private keys are never stored unencrypted — the Oyster wallet uses BoltDB with
AES-256 encryption keyed from your passphrase via scrypt (via
snacl). - Biometric authentication (FaceID / Fingerprint) gates the passphrase unlock in the Transact screen.
- The wallet database lives in
Context.filesDir/oyster/wallet/wallet.db— excluded from Android cloud backup viabackup_rules.xml. - TLS is enforced for all HTTP traffic (
network_security_config.xml); cleartext is blocked. signing/keystore.propertiesandsigning/production.keystoreare in.gitignore. Never commit them.
License
Pearl node and wallet source: ISC License — see wallet-go/LICENSE.
Android app code: © 2026 Robin L. M. Cheung, MBA. All rights reserved.