The Unbound wallet for Android and iOS: see what your store credit is worth, review a real quote, and sign on your own device.
Part of Unbound - turn stranded value into something usable.
Headless renders of the real screens with synthetic Testnet fixtures (tool/screenshots), not device captures.
Store credit is usually stuck inside one retailer. Unbound lets a person exchange store credit that a retailer has genuinely issued and agreed to honor for other Stellar assets, at a real order-book quote that they sign on their own device, or redeem it with the issuer.
- Issuer-authorized only. A verified merchant signs a redemption policy with its Stellar issuer key and must authorize every holder's trustline. No issuer, no usable credit; arbitrary gift cards are out of scope.
- No fake liquidity. Quotes come from real Stellar path payments, expire and carry a minimum-output floor. No route means no quote. Unbound never promises a price, liquidity or fiat cash-out.
- Ledger-truthful. Nothing is shown as settled until the ledger confirms it; unknown outcomes reconcile by hash.
- Testnet only today. Mainnet needs an external security review and legal/compliance work that has not happened.
A Flutter app (Riverpod, go_router) that holds the user's Testnet key in the platform keystore and never sends it anywhere. It shows credit inventory with issuer and redemption terms, requests quotes, decodes the unsigned transaction locally and checks it against the quote before asking for biometric approval and signing, and tracks exchange and redemption status honestly (pending, reconciling, confirmed, failed), surviving restarts.
Unbound is four independently tested repositories with one owner per boundary. This one is highlighted.
flowchart LR
M["Unbound-mobile<br/>Flutter wallet"]
S["Unbound-site<br/>landing + merchant console"]
C["Unbound-core<br/>HTTP API v1, policy, intents, audit"]
G["Unbound-stellar<br/>SDK, gateway service, chain worker"]
N[("Stellar Testnet<br/>assets, trustlines, DEX")]
M -- "quotes, intents, signed XDR" --> C
S -- "merchant auth, redemption queue" --> C
C -- "quote / build / submit" --> G
G -- "Horizon" --> N
G -- "reconciliation events" --> C
style M fill:#7537F5,stroke:#10092E,color:#ffffff
Clients never build Stellar transactions, and core never reimplements ledger logic. Details:
docs/ARCHITECTURE.md and docs/STELLAR_PROTOCOL.md.
Requires the Flutter stable SDK with Dart 3.13 or newer.
flutter pub get
dart format --output=none --set-exit-if-changed lib test
flutter analyze --fatal-infos
flutter test
flutter run --dart-define=API_URL=http://10.0.2.2:3001 # Android emulator -> a local Unbound-coreflutter run has not been exercised in the authoring sandbox (no emulator or Android SDK); the analyzer and the full
test suite, including widget journeys with real fixture XDR, run in CI.
Pushing a tag v* runs .github/workflows/release.yml: Java 17 + Flutter,
analyze, tests, flutter build apk --release, and a normal (latest, non-prerelease) release on
GitHub Releases with two assets whose names never change:
unbound-android.apk and unbound-android.apk.sha256. So this link always downloads the newest build (the site's
Download for Android button uses it):
https://github.com/Unbound224/Unbound-mobile/releases/latest/download/unbound-android.apk
To install: allow "Install unknown apps" for your browser or file manager, open the APK, tap Install. Verify with
sha256sum -c unbound-android.apk.sha256. This workflow has not run yet (it was written without access to an
Android SDK), so the link resolves only after the first v* tag is pushed. The launcher name is "Unbound" and the
icon is generated from brand/icon.svg (checked by test/brand_assets_test.dart).
Signing: by default the APK is debug-signed (sideloading only; the release notes say which). To sign with your own key, add these repository secrets:
| Secret | Value |
|---|---|
ANDROID_KEYSTORE_BASE64 |
base64 -w0 upload-keystore.jks |
ANDROID_KEYSTORE_PASSWORD |
keystore password |
ANDROID_KEY_ALIAS |
key alias |
ANDROID_KEY_PASSWORD |
key password |
Optionally set the repository variable UNBOUND_API_URL to an HTTPS Unbound-core URL baked into the build;
otherwise the APK uses the emulator default, which does not work on a real phone. The app is not in any app store.
Legend: Built = implemented and covered by automated tests in this repo. Not yet = not started or not verified.
| Layer | Status |
|---|---|
Pure rules: exact Amount, strict contract models, QuoteReview (re-derived slippage floor), inventory, status text |
Built |
ApprovalGuard + StellarXdrInspector: local decode of the unsigned envelope compared with the quote |
Built; tested on real XDR from Unbound-stellar's builders |
SecureWalletSigner (Android Keystore / iOS Keychain via flutter_secure_storage), Testnet only |
Built; Dart signatures match the TypeScript fixture byte for byte. Platform keystore unverified on device |
LocalAuthApproval (biometric / device credential, fails closed) |
Written; unverified on device |
HttpCoreApi and durable pending store |
Built; tested against a loopback stub, not the real core |
| Screens: wallet gate, inventory, exchange, quote review with countdown, status, redemption, settings | Built; widget-tested incl. expiry, refusal, cancel, unknown outcome |
Escrowed redemptions: local decode of the Soroban open_return / expire_refund (contract, function, wallet, issuer, amount, sha256 redemption key, deadline) before signing; escrow card, deadline, Check status, Take my credit back |
Built; tested with real XDR from Unbound-stellar's builders. Never run against a deployed contract |
| Wallet backup (recovery key behind acknowledgement + device approval) and restore from recovery key | Built; unit and widget tested. Android screenshot / recording / recents-thumbnail blocking (FLAG_SECURE) while the recovery key screen is open: channel code and tests built, not verified on a device; iOS has no equivalent and is not covered |
| Brand: app name, adaptive launcher icon, splash on brand ink | Built (generated with flutter_launcher_icons) |
Release workflow: v* tag -> latest (non-prerelease) GitHub Release with fixed unbound-android.apk + .sha256 |
Written and guarded by a test; not run yet (no tag pushed) |
flutter build apk --release on Windows (2026-11-04): succeeds (52.5 MB debug-signed APK) after fixing the plugin compileSdk (see CHANGELOG); never installed or run on a phone or emulator |
Built; not run on a device |
| Device / integration tests, TalkBack/VoiceOver audit, localisation, push | Not yet |
| App Store / Google Play listings | Not yet: not in any store |
lib/domain (pure rules) · lib/flows (orchestration and ports) · lib/integrations (HTTP, XDR, signer, stores) ·
lib/app + lib/features (Riverpod and screens) · test/ (test/support/fakes.dart and test/app/harness.dart are
test adapters only) · tool/ (brand icon rendering, README screenshots).
| Repository | Owns | Stack | CI |
|---|---|---|---|
| Unbound-core | Backend API: issuers, policies, intents, redemptions, audit | TypeScript, Node 22, SQLite | |
| Unbound-stellar | Stellar SDK, gateway service, chain worker, Soroban redemption escrow | TypeScript, @stellar/stellar-sdk, Rust/Soroban | |
| Unbound-site | Landing page, partner docs and merchant console | Next.js 16, React 19, Tailwind 4 | |
| Unbound-mobile (this repo) | Flutter consumer wallet | Flutter, Dart, Riverpod |
Contributions are welcome. Start with CONTRIBUTING.md, then pick from the scoped, testable ideas in
docs/CONTRIBUTOR_ISSUES.md. Unbound intends to take part in
Drips Wave; that is subject to Drips' review and
limits, and no rewards are promised. Wave issues are opened only once their dependencies and acceptance tests are ready.
Everyone agrees to the Code of Conduct.
Please report vulnerabilities privately (Security tab, Report a vulnerability) as described in
SECURITY.md. The threat model is in docs/SECURITY.md. Testnet only: never use
real funds, real customer data or Mainnet keys with this code.
docs/PRD.md (product) · docs/SRD.md (requirements, API, states) ·
docs/ARCHITECTURE.md · docs/TECHNICAL_BUILD.md ·
docs/ROADMAP.md · docs/BRAND.md · AGENTS.md (rules for coding agents).
The canonical copy of the shared docs lives in Unbound-core.
MIT. The Unbound logo in brand/ is the project's approved identity and is shared by all four
repositories; it has not been trademark-cleared.


