No description
Find a file
Robin L. M. Cheung, MBA 7ace444642 docs(problems): rename screenshots + add triage index; feat(ui): splash, about, network/wallet dialogs; chore(build): auto-version on compile
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
2026-05-31 15:35:15 -04:00
.claude-flow/data Unify namespace to mba.robin.prlwallet; fix build blockers 2026-05-27 00:06:40 +00:00
app docs(problems): rename screenshots + add triage index; feat(ui): splash, about, network/wallet dialogs; chore(build): auto-version on compile 2026-05-31 15:35:15 -04:00
dist docs(problems): add UI issue screenshots and branding assets 2026-05-31 13:51:05 -04:00
DOCS docs(problems): rename screenshots + add triage index; feat(ui): splash, about, network/wallet dialogs; chore(build): auto-version on compile 2026-05-31 15:35:15 -04:00
gradle checkpoint: working signed Android build + vendor wallet/build pkg + version stamper 2026-05-30 00:51:18 -04:00
LOGS docs(problems): add UI issue screenshots and branding assets 2026-05-31 13:51:05 -04:00
scripts docs(problems): rename screenshots + add triage index; feat(ui): splash, about, network/wallet dialogs; chore(build): auto-version on compile 2026-05-31 15:35:15 -04:00
signing checkpoint: working signed Android build + vendor wallet/build pkg + version stamper 2026-05-30 00:51:18 -04:00
wallet-go WIP: local UI wiring + brand assets (pre-merge) 2026-05-31 06:29:21 -04:00
web-icons Add prldroid logo icons and bump to v1.10 2026-05-31 06:20:35 -04:00
.env WIP: local UI wiring + brand assets (pre-merge) 2026-05-31 06:29:21 -04:00
.env.example feat(wallet): add second referral code to round-robin (ref_01e7cj3G) 2026-05-30 10:01:44 -04:00
.gitignore WIP: local UI wiring + brand assets (pre-merge) 2026-05-31 06:29:21 -04:00
agentdb.rvf docs(problems): add UI issue screenshots and branding assets 2026-05-31 13:51:05 -04:00
agentdb.rvf.lock docs(problems): add UI issue screenshots and branding assets 2026-05-31 13:51:05 -04:00
build.gradle.kts checkpoint: working signed Android build + vendor wallet/build pkg + version stamper 2026-05-30 00:51:18 -04:00
CHECKLIST.md WIP: local UI wiring + brand assets (pre-merge) 2026-05-31 06:29:21 -04:00
gradle.properties Initial Pearl Wallet Android — gomobile bridge to pearl-ai-mining 2026-05-26 20:02:24 +00:00
local.properties.template checkpoint: working signed Android build + vendor wallet/build pkg + version stamper 2026-05-30 00:51:18 -04:00
prldroid-icon-square.png Add prldroid logo icons and bump to v1.10 2026-05-31 06:20:35 -04:00
prldroid-logo.png Add prldroid logo icons and bump to v1.10 2026-05-31 06:20:35 -04:00
README.md Unify namespace to mba.robin.prlwallet; fix build blockers 2026-05-27 00:06:40 +00:00
ROADMAP.md WIP: local UI wiring + brand assets (pre-merge) 2026-05-31 06:29:21 -04:00
settings.gradle.kts Initial Pearl Wallet Android — gomobile bridge to pearl-ai-mining 2026-05-26 20:02:24 +00:00
version.json docs(problems): rename screenshots + add triage index; feat(ui): splash, about, network/wallet dialogs; chore(build): auto-version on compile 2026-05-31 15:35:15 -04:00
version.txt docs(problems): rename screenshots + add triage index; feat(ui): splash, about, network/wallet dialogs; chore(build): auto-version on compile 2026-05-31 15:35:15 -04:00

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. PearlExplorerApihttps://lordofpearls.xyz, OtcPricingApihttps://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 (~1525 MB, first build ~3 min)

What the script does:

  1. Checks wallet-go/go.mod and wallet-go/androidbridge/bridge.go exist
  2. Installs gomobile / gobind if absent
  3. Runs gomobile init
  4. Runs gomobile bind -target=android -androidapi 26 -javapkg mba.robin.prlwallet binding the androidbridge package 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 via backup_rules.xml.
  • TLS is enforced for all HTTP traffic (network_security_config.xml); cleartext is blocked.
  • signing/keystore.properties and signing/production.keystore are 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.