{"id":51029259,"url":"https://github.com/nighthawk-apps/nighthawk-android-wallet","last_synced_at":"2026-06-21T22:30:57.007Z","repository":{"id":148886295,"uuid":"613462304","full_name":"nighthawk-apps/nighthawk-android-wallet","owner":"nighthawk-apps","description":"Nighthawk Wallet 3.0","archived":false,"fork":false,"pushed_at":"2026-06-06T21:14:27.000Z","size":29404,"stargazers_count":17,"open_issues_count":3,"forks_count":2,"subscribers_count":2,"default_branch":"nighthawk-dark","last_synced_at":"2026-06-06T23:11:12.527Z","etag":null,"topics":["zcash","zcash-wallet"],"latest_commit_sha":null,"homepage":"https://nighthawkwallet.com","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"zodl-inc/zodl-android","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nighthawk-apps.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"docs/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-03-13T16:03:34.000Z","updated_at":"2026-06-06T21:18:11.000Z","dependencies_parsed_at":"2023-09-24T17:08:47.642Z","dependency_job_id":null,"html_url":"https://github.com/nighthawk-apps/nighthawk-android-wallet","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/nighthawk-apps/nighthawk-android-wallet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nighthawk-apps%2Fnighthawk-android-wallet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nighthawk-apps%2Fnighthawk-android-wallet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nighthawk-apps%2Fnighthawk-android-wallet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nighthawk-apps%2Fnighthawk-android-wallet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nighthawk-apps","download_url":"https://codeload.github.com/nighthawk-apps/nighthawk-android-wallet/tar.gz/refs/heads/nighthawk-dark","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nighthawk-apps%2Fnighthawk-android-wallet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34628453,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-21T02:00:05.568Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["zcash","zcash-wallet"],"created_at":"2026-06-21T22:30:56.901Z","updated_at":"2026-06-21T22:30:56.988Z","avatar_url":"https://github.com/nighthawk-apps.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Nighthawk Wallet (DarkFi Edition)\n\nPrivacy-preserving wallet (work-in-progress) by [Nighthawk Apps](https://nighthawkapps.com). This branch ships as a **new Android application id** running on the DarkFi network with DRK currency. The app aims to integrates a fully working native DarkFi wallet API via **UniFFI** (`rust/darkfi-mobile-ffi` → generated Kotlin + `DarkfiMobileFfiApi`), allowing for complete chain synchronization, transaction broadcasting, and robust native interactions.\n\n## Download (placeholders)\n\nStore listings are **not finalized** for `com.nighthawkwallet.android`. Badge targets below are explicit placeholders until publishing completes:\n\n- **F-Droid:** `https://PLACEHOLDER_FDROID_PACKAGE_URL`\n- **Google Play:** `https://PLACEHOLDER_PLAY_STORE_LISTING_URL`\n\nReplace these URLs in documentation when production listings exist—do not invent live links prematurely.\n\n## What to run locally\n\nFrom the repository root, in order (first-time or after vendored DarkFi / native code changes):\n\n```bash\n# 1) Pin upstream DarkFi (once, or when docs/upstream/darkfi-revision.txt changes)\n./scripts/vendor-darkfi.sh\n\n# 2) Android NDK path (adjust version to your SDK install)\nexport ANDROID_NDK_HOME=\"$HOME/Library/Android/sdk/ndk/$(ls \"$HOME/Library/Android/sdk/ndk\" | sort -V | tail -1)\"\n\n# 3) UniFFI wallet native lib (required for balances / send / scan)\n./scripts/build-darkfi-mobile-ffi-android.sh\n# Emulator-only ABIs (faster): MOBILE_FFI_ABIS=\"arm64-v8a x86_64\" ./scripts/build-darkfi-mobile-ffi-android.sh\n\n# 4) Embedded DarkIRC binary (required for in-app Chat without desktop darkirc)\n./scripts/build-darkirc-android.sh\n# Compiles zkas proofs + cross-builds darkirc; outputs artifacts/darkirc/\u003cabi\u003e/darkirc_exec\n\n# 5) Optional: embedded darkfid (local chain node in APK)\n# ./scripts/build-darkfid-android.sh\n\n# 6) Assemble and install testnet debug (side-by-side .testnet package)\n./gradlew :app:assembleDarkfitestnetDebug\nadb install -r app/build/outputs/apk/darkfitestnet/debug/app-darkfitestnet-debug.apk\n\n# 7) Quick checks\n./gradlew :darkfi-android-sdk:testDebugUnitTest\n```\n\n**Chat on emulator:** open **Chat** with **Run embedded DarkIRC** enabled (default). IRC shows **Connected** once loopback `:6667` is up; **public channel history** needs P2P/DAG sync inside `darkirc` (can take several minutes on first launch). Watch `adb logcat -s darkirc-stdout` for `[P2P]` seed connects and DAG sync. Increase **DAG hours** in **Chat → settings** if you need more history (1–24).\n\n**Desktop darkirc instead of embedded:** run upstream `darkirc` on the workstation, then `adb reverse tcp:6667 tcp:6667`, disable embedded DarkIRC in Chat settings, and point IRC host to `127.0.0.1:6667`.\n\nDisk note: `third_party/darkfi/target/` can grow very large; `cargo clean` there frees space between rebuilds.\n\n## Build the project\n\nEnd-to-end build steps for CLI users. Android Studio installation, emulator setup, signing keys, **build variant** UI, and troubleshooting are covered in **[Setup Documentation](docs/Setup.md)**—use that guide for JDK/SDK/`ANDROID_HOME`, devices, and IDE troubleshooting.\n\n### Prerequisites\n\n- **JDK 17 or newer** (Temurin is a common distribution; Gradle may use toolchains — see Setup).\n- **Android SDK**: install via Android Studio’s SDK Manager, set **`ANDROID_HOME`**, and add **`platform-tools`** to `PATH` for `adb`.\n- **Android NDK**: install an NDK revision side-by-side with the SDK (`$ANDROID_HOME/ndk/\u003cversion\u003e`). **`ANDROID_NDK_HOME`** must point at that folder when using **`cargo-ndk`** (Rust cross-build).\n- **Rust** ([rustup](https://rustup.rs/)) and **Cargo**.\n- **`cargo-ndk`**: `cargo install cargo-ndk`\n- **Rust Android std targets** (run once after installing Rust):\n\n  ```bash\n  rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android\n  ```\n\n### 1. Build the UniFFI native libraries\n\nThis produces **`libdarkfi_mobile_ffi.so`** for each ABI and copies it into **`artifacts/mobile-ffi/`** and **`darkfi-android-sdk/src/main/jniLibs/`** (Gradle **`syncMobileFfiArtifacts`** merges artifacts into the APK).\n\n```bash\nexport ANDROID_NDK_HOME=/path/to/ndk   # e.g. ~/Library/Android/sdk/ndk/29.0.14206865\n./scripts/build-darkfi-mobile-ffi-android.sh\n\n# Emulator + device only (saves disk/time):\n# MOBILE_FFI_ABIS=\"arm64-v8a x86_64\" ./scripts/build-darkfi-mobile-ffi-android.sh\n```\n\nIf **`artifacts/mobile-ffi`** or **`jniLibs`** already contain matching **`.so`** files, skip rebuilding until Rust or the UDL changes.\n\n\u003cdetails\u003e\n\u003csummary\u003eManual cargo-ndk copy (equivalent to the script)\u003c/summary\u003e\n\n```bash\nexport ANDROID_NDK_HOME\ncd rust\ncargo ndk \\\n  -t arm64-v8a -t armeabi-v7a -t x86 -t x86_64 \\\n  build --release -p darkfi-mobile-ffi\n\nJNILIBS=../darkfi-android-sdk/src/main/jniLibs\nT=target\nmkdir -p \"$JNILIBS/arm64-v8a\" \"$JNILIBS/armeabi-v7a\" \"$JNILIBS/x86\" \"$JNILIBS/x86_64\"\ncp \"$T/aarch64-linux-android/release/libdarkfi_mobile_ffi.so\"   \"$JNILIBS/arm64-v8a/\"\ncp \"$T/armv7-linux-androideabi/release/libdarkfi_mobile_ffi.so\" \"$JNILIBS/armeabi-v7a/\"\ncp \"$T/i686-linux-android/release/libdarkfi_mobile_ffi.so\"       \"$JNILIBS/x86/\"\ncp \"$T/x86_64-linux-android/release/libdarkfi_mobile_ffi.so\"   \"$JNILIBS/x86_64/\"\ncd ..\n```\n\n\u003c/details\u003e\n\nDetails and **UniFFI Kotlin regeneration** when editing **`darkfi_mobile_ffi.udl`**: [`rust/darkfi-mobile-ffi/README.md`](rust/darkfi-mobile-ffi/README.md).\n\n**Feature catalog (iOS port / parity):** [`docs/app-features.md`](docs/app-features.md) · **Implementation plan (P0–P4):** [`docs/implementation-plan.md`](docs/implementation-plan.md).\n\n### 2. Build the Android app\n\nFrom the repository root:\n\n```bash\n# Debug APK — mainnet flavor (production applicationId)\n./gradlew :app:assembleDarkfimainnetDebug\n\n# Debug APK — testnet flavor (distinct applicationId `.testnet`, can install alongside mainnet)\n./gradlew :app:assembleDarkfitestnetDebug\n\n# Build all conventional outputs (Gradle default; may assemble multiple variants)\n./gradlew assembleDebug\n\n# Release APK (requires a configured release keystore; see docs/Setup.md)\n# ./gradlew :app:assembleDarkfimainnetRelease\n```\n\nOutput APKs appear under **`app/build/outputs/apk/`** (path includes flavor and build type). Install with `adb install -r path/to.apk`.\n\n### 3. Optional checks before you ship a change\n\nQuick validation (see **[Verification](#verification-local--ci-parity)** for the full parity table):\n\n```bash\n./gradlew :darkfi-android-sdk:testDebugUnitTest\n./gradlew :app:assembleDarkfimainnetDebug\n```\n\n## Chat (DarkIRC-style)\n\nIn-app **Chat** uses a Kotlin IRC client against DarkIRC’s listener (`CAP` / `NICK` / `USER` / `JOIN` / `PRIVMSG`). When enabled, the app can run a **packaged `darkirc_exec`** in a foreground service (loopback `127.0.0.1:6667`) — see [DarkIRC embedded Android](docs/darkirc-embedded-android.md). That is **not** upstream’s in-process **`DarkIrc` plugin** (Event Graph + P2P inside the same Rust binary); full P2P/DAG/ChaCha parity remains follow-up — [DarkFi integration](docs/darkfi-integration.md), [DarkIRC upstream](docs/darkfi-chat-upstream.md).\n\n| Capability | Behavior |\n|------------|----------|\n| Default endpoint | `127.0.0.1:6667` (pair with workstation `darkirc` + `adb reverse tcp:6667 tcp:6667`) |\n| Tor toggle | Persists “prefer Tor” for seed hints and routes **non-loopback** IRC through **SOCKS5** at `127.0.0.1:9050` (default bundled tor-android listener) |\n| SOCKS readiness | Embedded Tor starts in-process when enabled; the client waits up to **90s** for the SOCKS port to accept TCP, probing **9050** / **9150** as needed |\n| Loopback IRC | SOCKS is **skipped** when the IRC host is loopback so USB reverse + desktop `darkirc` works |\n| Auto-reconnect | On `ON_RESUME`, reconnects only when **not** already connected (direct or via Tor) |\n| DAG history | Server replays event-graph `PRIVMSG` on connect (do **not** request DarkIRC `no-history` cap); needs P2P sync for non-empty channels |\n| Chat settings | **Settings → Chat** exposes identity phrase/public ID, IRC endpoint, Tor/SOCKS, reconnect |\n| Identity \u0026 IRC PASS | Stored with **EncryptedSharedPreferences** (legacy plaintext prefs migrated on upgrade); KeyStore failures fall back to app-private prefs and log `DarkfiChat` |\n| Cleartext traffic | Allowed app-wide so plain TCP IRC / loopback SOCKS work—HTTPS endpoints still use TLS |\n\n### Build and test (chat-related)\n\nFrom the repo root:\n\n```bash\n# Compile debug APK (example flavor)\n./gradlew :app:assembleDarkfitestnetDebug\n\n# Unit tests for IRC parser, Tor routing, SOCKS probe, and minimal DarkIRC handshake (JVM; handshake uses Robolectric for `android.util.Log`)\n./gradlew :darkfi-android-sdk:testDebugUnitTest\n```\n\nManual smoke tests are described in [DarkIRC chat on Android](docs/android-darkirc-chat.md) (desktop `darkirc`, `adb reverse`, and Tor + non-loopback IRC).\n\n## Verification (local / CI parity)\n\nCommands mirror [.github/workflows/pull-request.yml](.github/workflows/pull-request.yml) where tooling resolves:\n\n| Step | Command |\n|------|---------|\n| JVM libraries | `./gradlew :configuration-api-lib:check :preference-api-lib:check :spackle-lib:check` |\n| Wallet SDK unit tests | `./gradlew :darkfi-android-sdk:testDebugUnitTest` |\n| Repo JVM test aggregation | `./gradlew test` (modules without tests report `NO-SOURCE`) |\n| DarkFi mainnet debug APK | `./gradlew :app:assembleDarkfimainnetDebug` |\n| Android Lint (release model) | `ORG_GRADLE_PROJECT_IS_MINIFY_ENABLED=false ./gradlew :app:lintDarkfimainnetRelease` |\n\nNotes:\n\n- `./gradlew checkProperties` validates signing-related Gradle props—expects CI/release defaults when that task is enabled.\n- `./gradlew ktlint` resolves **`com.pinterest.ktlint:ktlint-cli:\u003cversion\u003e:all`** (see **`KTLINT_VERSION`** in `gradle.properties`; fat jar for the CLI). Maven Central is used, with `repo.maven.apache.org` duplicated as a narrow mirror for the `com.pinterest.ktlint` group.\n- `./gradlew detektAll` runs in CI; a repo-wide clean pass requires clearing the current Detekt weighted-issue backlog.\n\n## Architecture (current)\n\n- **UI:** `ui-lib` Compose flows for onboarding, backup, restore, send (mock), receive, settings, chat diagnostics.\n- **Themes:** Advanced settings expose three variants—**Stealth (default)**, **Light**, and **Midnight** (`AppThemeVariant`, persisted as `APP_THEME_VARIANT`; legacy dark toggle migrates on read).\n- **Settings hub:** Main settings surface includes optional **identity**—IRC-style nickname, chat **public ID** (copy/share), and gated **secret phrase** reveal for chat (`SettingsHubIdentitySection`); distinct from the wallet recovery seed.\n- **Wallet SDK:** `darkfi-android-sdk` provides `PersistableDarkfiWallet`, BIP-39 loading/validation (`DarkfiMnemonic`, 24-word default), `DarkfiWalletCoordinator`, and `DarkfiSynchronizer` (real-time balance and transaction synchronization).\n- **Balance model:** A single **confirmed / spendable** DRK tally flows through `DarkfiSynchronizer.confirmedBalanceAtomic` → `WalletSnapshot`—no transparent vs shielded split (aligned with DarkFi’s private ledger model).\n- **RPC presets:** “Change server” maps to **`DarkfiEndpoint`** (`tcp://` URL style; ports match upstream `bin/drk/drk_config.toml` darkfid defaults—mainnet **8345**, testnet **18345**; legacy **26660/26670** in stored JSON auto-migrate), persisted via `EncryptedPreferenceKeys.DARKFI_ENDPOINT_PRESET` alongside wallet JSON.\n- **Native DarkFi:** **UniFFI + JNA** bindings live under `com.nighthawkapps.lib.uniffi.darkfi_mobile_ffi`; runtime calls load **`libdarkfi_mobile_ffi.so`** from **`darkfi-android-sdk/src/main/jniLibs/\u003cabi\u003e/`**. Rebuild and recopy whenever **`rust/darkfi-mobile-ffi`** changes—see **[Build the project](#build-the-project)**. See also **UniFFI \u0026 native bridge** below and [DarkFi integration](docs/darkfi-integration.md).\n\n## UniFFI \u0026 native bridge\n\n| Piece | Role today |\n|-------|------------|\n| **`rust/darkfi-mobile-ffi`** | Real UniFFI `cdylib` (`libdarkfi_mobile_ffi`; UDL `src/darkfi_mobile_ffi.udl`). Builds with `cargo` from **`rust/`** workspace ([crate README](rust/darkfi-mobile-ffi/README.md)). Fully linked to the upstream DarkFi workspace, providing complete wallet API bindings via FFI. |\n| **Generated Kotlin** | `darkfi-android-sdk/.../uniffi/darkfi_mobile_ffi/darkfi_mobile_ffi.kt`, package `com.nighthawkapps.lib.uniffi.darkfi_mobile_ffi`, regenerated with **`uniffi-bindgen`** (commands in crate README). |\n| **`DarkfiMobileFfiApi`** | Stable Kotlin façade (`com.nighthawkapps.lib.android.sdk.uniffi`) so app/UI code avoids importing generated symbols directly. |\n| **`rust/darkfi-android-bridge`** | Older minimal `cdylib`/`darkfi_android_bridge`; kept for incremental experiments—**prefer `darkfi-mobile-ffi`** for UniFFI ([crate README](rust/darkfi-android-bridge/README.md)). |\n| **`darkfi-android-sdk` JNA dependency** | UniFFI 0.31 Kotlin bindings use JNA (`net.java.dev.jna:jna`). |\n| **`darkfi-android-sdk/src/main/jniLibs/`** | **`libdarkfi_mobile_ffi.so`** per ABI (see [jniLibs README](darkfi-android-sdk/src/main/jniLibs/README.md)); Gradle does **not** invoke Cargo automatically—use **`cargo-ndk`** ([Build the project](#build-the-project)). |\n\n**Production mapping:** Mirrors DarkFi wallet RPC / `bin/drk` semantics—single balance field, scan/progress, broadcast. The **`DarkfiSynchronizer`** is fully implemented with native Rust-backed clients. `./gradlew :darkfi-android-sdk:testDebugUnitTest` covers Kotlin helpers and chain correctness via the native path.\n\n## Additional setup (Android Studio)\n\nJDK/SDK install order, device debugging, IDE **build variants**, and Gradle troubleshooting: [Setup Documentation](docs/Setup.md).\n\n## Reporting an issue\n\nSecurity disclosures: [GitHub Security Advisories](https://github.com/nighthawk-apps/nighthawk-android-wallet/security) or maintainer support channels.\n\nTechnical issues / features: file a GitHub issue on this repository.\n\nGeneral support: [DM @NighthawkWallet on X](https://x.com/nighthawkwallet).\n\n## Contributing\n\nRead [Contributing Guidelines](docs/CONTRIBUTING.md).\n\nTranslations: [Crowdin](https://crowdin.com/project/nighthawk-wallet/) [![Crowdin](https://badges.crowdin.net/nighthawk-wallet/localized.svg)](https://crowdin.com/project/nighthawk-wallet)\n\n## Known issues\n\n1. Intel-based machines may need `WALLET_IS_DEPENDENCY_LOCKING_ENABLED=false` in `~/.gradle/gradle.properties` if Gradle locking flakes appear during IDE sync.\n2. Gradle may warn about mixed AGP detection for composite builds—safe to ignore when versions match under `build-conventions-*`.\n3. Enabling `IS_ANDROID_INSTRUMENTATION_TEST_COVERAGE_ENABLED` prevents interactive debugging of the debug APK (CI-only flag).\n4. Compose + Jacoco coverage interaction remains limited upstream.\n\n## Install notes\n\nThis tree does **not** migrate data from older forks or package IDs—every install is treated as a new DarkFi-oriented wallet; restore from your backed-up seed phrase if needed.\n\n## Shortcuts\n\n`ui-lib/src/main/res/ui/common/xml/shortcuts.xml` hard-codes `android:targetPackage=\"com.nighthawkwallet.android\"` because shortcuts XML does not honor `${applicationId}` substitution. Flavor-specific variants require codegen or Gradle template expansion—until then, avoid shipping mismatched testnet shortcuts silently.\n\n## Disclosure policy\n\nDo not disclose vulnerabilities publicly before coordinated disclosure with maintainers. Encrypt mail where possible.\n\n## Disclaimers\n\n- Funding/on-ramp and third-party exchange shortcuts were removed; acquire DRK through channels you trust.\n- Chat connects to **DarkIRC-style IRC over TCP** (Kotlin client); **Arti/Tor inside the daemon** is not in this APK until JNI links DarkFi—loopback **SOCKS** (bundled tor-android or your configured proxy) covers the Android IRC hop when Tor is on and the IRC host is non-loopback.\n- Fiat hints depend on public APIs (`COIN_GECKO_ASSET_ID`) and may be unavailable.\n\n## Contact Nighthawk Apps\n\nEmail: `nighthawkwallet@protonmail.com` (see disclosure section for PGP expectations).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnighthawk-apps%2Fnighthawk-android-wallet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnighthawk-apps%2Fnighthawk-android-wallet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnighthawk-apps%2Fnighthawk-android-wallet/lists"}