AURA Wallet — Setup & Integration Guide
1 Overview
AURA Wallet is a self-custody, multi-chain crypto wallet source-code product covering Ethereum, Base, BNB Chain, TRON, Solana and Bitcoin. The product is a monorepo with four parts that this guide walks through in order: the mobile app (React Native, iOS/Android), the backend and Admin Control Center (Next.js on Vercel + MongoDB), a small set of background workers (Vercel Cron + QStash), and the native signer module that holds private-key material outside JavaScript.
This document is written for a developer who is comfortable with React Native and Node.js tooling but has not seen this codebase before. Follow the sections in order the first time through.
2 What's Included
Mobile app source
Full React Native TypeScript source, native iOS and Android projects, all wallet screens (Portfolio, Send, Receive, Swap, DApp Browser, WalletConnect, Notifications, Settings, AURA Entity chat).
Backend & Admin
Next.js API routes for device auth, balances, prices, history, swap quotes, TRON gasless gating, remote config, and the full Admin Control Center UI.
Chain adapters
packages/chains: derivation paths, transaction building, decoding and fee logic shared between the app and backend for every supported network.
Native signer module
Real, hand-reviewed @aura/signer-native: BIP‑39/32/SLIP‑10 derivation, PIN-gated encrypted seed storage and transaction/message signing on both platforms. See Section 9 for the full API surface and implementation notes.
3 Architecture
The codebase follows a hexagonal architecture: domain logic knows nothing about specific chains or vendors, and everything chain- or vendor-specific sits behind an adapter.
repo/
├── app/ React Native app (iOS + Android)
│ └── src/ application (ports/use-cases) · infrastructure (adapters) · presentation (UI)
├── web/ Next.js backend (public API) + Admin Control Center
├── worker/ Vercel Cron handlers + QStash consumers (indexers, retries, price watcher)
├── packages/
│ ├── chains/ Chain adapters: derivation, tx building, decode — shared by app + web + worker
│ ├── domain/ Pure domain types (Asset, Tx, FeeQuote, Verdict…)
│ ├── shared/ i18n resources, constants, utils
│ └── signer-native/ Native signing module (Nitro Module, Kotlin/Swift) — see Section 9
└── spec/, docs/ Module specs and architecture decision records
| Invariant | What it means for you |
|---|---|
| Keys never enter JS | All key material lives in native code. JavaScript sends unsigned transaction bytes in and receives signatures out — never a private key or seed. |
| One signing pipeline | Send screen, WalletConnect and the in-app DApp browser all route through the same decode → review → sign flow. There is no alternate "fast path" that skips review. |
| Read via backend, write direct | The app reads balances/prices/history through your backend (cached, provider-backed). It broadcasts signed transactions directly to chain RPCs, not through your backend. |
| Fees are transparent | Platform commission is always shown as its own labeled line in every quote and confirmation screen — never folded into the displayed rate. |
4 Prerequisites
| Tool | Version | Used for |
|---|---|---|
node | ≥ 20 | Everything |
pnpm | 10.x (see packageManager in package.json) | Monorepo package management |
| Xcode | current stable, iOS 17 SDK+ | iOS build (CocoaPods included) |
| Android Studio / JDK 17 | current stable | Android build (minSdk 29) |
| MongoDB | Atlas or self-hosted | Backend persistence |
| Upstash Redis | REST API | Nonce replay protection, rate limiting, challenge storage |
| Upstash QStash | — | Cron fan-out and background job delivery |
| Vercel account | — | Recommended hosting for web/ (Cron + serverless functions) |
You will also want accounts with the third-party data/quote providers used by the backend: Moralis (EVM + Solana balances/history/prices), TronGrid (optional, higher rate limit), CoinGecko (optional, higher rate limit), Helius (Solana transaction history), 0x (EVM swap quotes), Jupiter (Solana swap quotes) and OneSignal (push notifications). Each is optional in the sense that the corresponding feature degrades gracefully without it — see the table in Section 6.
5 Quick Start
pnpm i # install all workspace packages (pnpm workspaces)
pnpm lint && pnpm typecheck # sanity-check the checkout builds cleanly
pnpm test # unit tests across all packages
From here, set up the backend first (Section 6) — the mobile app needs a running API to talk to for anything beyond static screens.
6 Backend & Admin Setup
The backend lives in web/ — a Next.js app that serves the device-facing /api/v1/* routes and the /admin Admin Control Center from one deployment.
6.1 Local development
cd web
cp .env.example .env.local # fill in the values described below
pnpm dev # pnpm --filter web dev, from the repo root
6.2 Environment variables
web/.env.example is the authoritative list with inline comments. The groups below summarize what each is for and what happens if it's left unset.
| Group | Variables | Required? | Effect if unset |
|---|---|---|---|
| Database | MONGODB_URI, MONGODB_DB_NAME | required | Every device-, address- and data-scoped endpoint fails; /api/ready reports Mongo unconfigured. |
| Redis | UPSTASH_REDIS_REST_URL, UPSTASH_REDIS_REST_TOKEN | required | Auth/rate limiting degrade; address-ownership challenges can be lost between serverless workers. |
| Background jobs | QSTASH_PUBLISH_URL, QSTASH_TOKEN, WORKER_SECRET | required | /api/ready reports QStash unconfigured and returns 503 even if Mongo/Redis are healthy. |
| Balances/history/prices | MORALIS_API_KEY | required | Every call to /api/v1/balances, /api/v1/history, /api/v1/prices fails. |
| Tron / price fallback | TRONGRID_API_KEY, COINGECKO_API_KEY | optional | Both providers still work unauthenticated at a lower rate limit if omitted. |
| Solana history | HELIUS_API_KEY | required for SOL history | Solana is the one chain whose history is not served by Moralis. Without this key, Solana history returns a clear "Helius API key" error. |
| Swap quotes | ZEROX_API_KEY (EVM), JUPITER_API_KEY (Solana) | optional | Omitting either simply removes that provider's pairs from the swap asset catalog — not an error. TRON/SunSwap needs neither. |
| Push notifications | ONESIGNAL_APP_ID, ONESIGNAL_REST_API_KEY | optional | Needed only when push is enabled. |
| Device attestation (Android) | GOOGLE_PLAY_INTEGRITY_PACKAGE_NAME, GOOGLE_PLAY_INTEGRITY_SERVICE_ACCOUNT_EMAIL, GOOGLE_PLAY_INTEGRITY_PRIVATE_KEY | required for real Android device registration | Android device registration fails 503 attestation_not_configured unless the dev bypass below is on. |
| Dev-only bypass | ALLOW_DEV_ATTESTATION_BYPASS | local dev only | When exactly true, accepts any attestation token on any environment — never set this in production. |
| Fee vaults | ETHEREUM_FEE_VAULT_ADDRESS, BASE_FEE_VAULT_ADDRESS, BNB_FEE_VAULT_ADDRESS, TRON_FEE_VAULT_ADDRESS, SOLANA_FEE_VAULT_ADDRESS, BITCOIN_FEE_VAULT_ADDRESS | required for commission | Public destination addresses (not private keys) that seed default per-chain fee policies on first run. |
| Admin bootstrap | ADMIN_USERNAME, ADMIN_EMAIL, ADMIN_DISPLAY_NAME, ADMIN_PASSWORD_HASH, ADMIN_TOTP_SECRET, ADMIN_TOTP_ENCRYPTION_KEY, ADMIN_ALLOWED_IPS | required | See Section 11 — these bootstrap the first Super Admin only, once. |
| Marketplace demo iframe | ADMIN_EMBEDDED_DEMO | optional, off by default | Set exactly true only for a hosted marketplace demo that must run inside preview.codecanyon.net. It permits that frame origin and uses a SameSite=None; Secure admin session cookie. Leave unset/false for normal buyer deployments, which keep framing denied and SameSite=Strict. |
| Operator tokens | CRON_SECRET, WORKER_SECRET, DEPLOYMENT_CHECK_TOKEN | required | Protect the cron entry point, worker delivery endpoint and deployment-verification endpoint respectively. |
| AI remote fallback | AI_REMOTE_ENABLED, AI_REMOTE_BASE_URL, AI_REMOTE_API_KEY, AI_REMOTE_MODEL, AI_REMOTE_TIMEOUT_MS | optional, off by default | See Section 10. |
6.3 Generating admin secrets
ADMIN_PASSWORD_PLAINTEXT='replace-with-a-long-password' node web/scripts/create-admin-credentials.mjs
node -e "console.log(require('node:crypto').randomBytes(32).toString('base64url'))" # ADMIN_TOTP_ENCRYPTION_KEY
Store the generated password hash and TOTP secret only in your deployment's secret manager — never commit the plaintext password. Enroll ADMIN_TOTP_SECRET in the operator's authenticator app before first login.
6.4 Deploying
- Push
web/to a Vercel project (or another Node host that supports Next.js route handlers and Vercel Cron-compatible scheduling). - Set every variable from the table above for the Preview and Production environments.
- Deploy, then confirm
web/vercel.jsonis detected so Cron is registered. - Follow the verification checklist in Section 12 before pointing a mobile build at the deployment.
7 Background Workers
worker/ holds the Vercel Cron handlers and QStash consumers. Each job is stateless: it reads a cursor from MongoDB, processes one batch, and writes the cursor back — safe to re-run and safe to run concurrently.
| Job | Cadence |
|---|---|
| TRON indexer | every 1 minute |
| Bitcoin indexer | every 1 minute |
| Undelegate / reclaim (TRON gasless) | every 5 minutes |
| Price watcher | every 1 minute |
| Fee retry | QStash-driven |
| Device garbage collection | daily |
| Balance-refresh tiers | hourly / daily |
No separate deployment step is required beyond deploying web/ with Cron and QStash configured (Section 6) — GET /api/cron/minutely is the single Vercel Cron entry point that fans out into QStash-delivered jobs against POST /api/workers.
8 Mobile App Setup
8.1 Environment
The app reads configuration via react-native-config. Create app/.env.dev and/or app/.env.prod with at least:
API_BASE_URL=https://your-backend-domain.example
API_TIMEOUT_MS=10000
WALLETCONNECT_PROJECT_ID=your-walletconnect-cloud-project-id
ONESIGNAL_APP_ID=your-onesignal-app-id
# Optional EVM RPC overrides — sane public defaults are used if omitted
ETHEREUM_RPC_URL=
BASE_RPC_URL=
BNB_RPC_URL=
API_BASE_URL must point at the backend you deployed in Section 6 — the app reads balances, prices and history through it, and registers each device against it.
8.2 Install
pnpm i # from the repo root, installs the whole workspace
cd app/ios && LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 pod install && cd ../..
8.3 Run in development
# from app/
pnpm ios:dev # ENVFILE=.env.dev react-native run-ios
pnpm android:dev # ENVFILE=.env.dev react-native run-android
8.4 Build a release package
# Android
pnpm apk:prod # ENVFILE=.env.prod ./gradlew assembleRelease
pnpm aab:prod # ENVFILE=.env.prod ./gradlew bundleRelease
# iOS
# Archive and export through Xcode (Product → Archive) using your own
# signing team and provisioning profiles — these are not included and
# must be created in your own Apple Developer account.
9 Native Signer Module
packages/signer-native is a real, fully implemented, hand-reviewed native module — not a placeholder. Both Android and iOS have genuine BIP‑39/BIP‑32/SLIP‑10 derivation, PIN-gated encrypted seed storage (Android Keystore / iOS Keychain), and transaction/message signing for the secp256k1 and ed25519 curves used across every supported chain, built on proven crypto libraries (libsecp256k1, libsodium) rather than hand-written primitives. It is built as a Nitro Module so the interface (src/specs/AuraSigner.nitro.ts) is a normal, readable TypeScript contract even though the implementation is native Kotlin/Swift.
9.1 API surface
This is the exact contract the rest of the app integrates against:
hasSeed(): Promise<boolean>
createSeed(pin): Promise<string[]> // mnemonic, returned once, for hold-to-reveal backup
importSeed(words, pin): Promise<void>
revealSeed(pin): Promise<string[]> // backup screen only
getPublicKeys(requests): Promise<PublicKeyResult[]>
unlock(pin): Promise<void>
lock(): Promise<void>
sign(path, curve, digest): Promise<ArrayBuffer>
signMessage(path, curve, message): Promise<ArrayBuffer>
wipe(): Promise<void>
Every method is async. curve is one of secp256k1 or ed25519. The app never calls this module directly — it goes through the hexagonal port pattern at app/src/application/signer/port/in/signer.port.ts, which keeps the SACRED signing surface small and independently reviewable from ordinary UI/native glue.
9.2 Non-negotiable rules this module follows
- Private keys and seeds never cross into JavaScript — JS sends unsigned bytes in, receives signatures out.
- Use only reviewed, real cryptographic libraries (e.g. libsecp256k1, libsodium,
@scure/*,@noble/*) — never hand-written or AI-generated primitives. - Never log seeds, private keys, or full addresses.
- Seed reveal screens require hold-to-reveal plus screenshot/recording protection (
FLAG_SECUREon Android, blur-on-snapshot on iOS).
9.3 Regenerating the Nitro glue
If you extend the interface, edit src/specs/AuraSigner.nitro.ts and regenerate the generated bindings:
pnpm --filter @aura/signer-native specs
10 AI Entity Configuration
The AURA Entity's AI-backed behavior is tiered, and only two tiers involve a language model at all — both optional. Tiers 0–2 (deterministic Send/Swap intent parsing, balance/history lookups, canned EN/VI explanations) never call a model.
10.1 Tier 3 — on-device model (optional)
A quantized Qwen 2.5 1.5B model, run fully on-device via react-native-executorch. Nothing is bundled at build time — the end user downloads it from Settings → Entity AI, and no wallet data leaves the device for this tier. Requires iOS 17+ / Android API 33+ and at least 4 GB RAM; on Android 29–32 the app keeps Tiers 0–2 and reports Tier 3 unsupported.
10.2 Tier 4 — remote fallback (optional, off by default)
Disabled in both the app and the backend until explicitly configured. To enable it, set on the backend:
AI_REMOTE_ENABLED=true
AI_REMOTE_BASE_URL=https://your-provider.example/v1 # must be HTTPS, OpenAI-compatible
AI_REMOTE_API_KEY=...
AI_REMOTE_MODEL=...
AI_REMOTE_TIMEOUT_MS=20000
The API key stays server-side and is never bundled into the mobile app. AI_REMOTE_BASE_URL is treated as an OpenAI-API-compatible base — bring any provider (OpenAI or a compatible self-hosted endpoint) that speaks that protocol; this product does not include or resell any third-party AI subscription. The user must also separately enable Tier 4 in Settings → Entity AI — an operator-side config alone does not turn it on for anyone.
Only a minimal, redacted context is ever sent for Tier 4: language, wallet label, asset/network/amount context, recent transaction metadata, and up to six prior text-only chat turns. Wallet addresses, counterparties, transaction hashes, device IDs, seed phrases, private keys and PINs are never sent, and the backend also rejects any address/hash/secret-looking text before it reaches the provider.
11 Admin Access & Roles
- Deploy with MongoDB healthy and every variable in the "Admin bootstrap" row of Section 6.2 set for Production.
- Call
GET /api/deployment/verifywithDEPLOYMENT_CHECK_TOKEN; confirmconfiguration.adminAuthistrue. - Open
/adminand sign in withADMIN_EMAIL(or the legacyADMIN_USERNAME), the password behindADMIN_PASSWORD_HASH, and the authenticator code fromADMIN_TOTP_SECRET. - The first authenticated request creates the four system roles (Super Admin, Operations Admin, Security Admin, Viewer) and the initial Super Admin, if
admin_usersis empty. - Confirm the bootstrapped account appears under
/admin/access/usersand the system roles under/admin/access/roles. - Create at least one additional Super Admin, then treat the bootstrap environment values as historical — changing them afterwards does not modify the Mongo-backed user record.
All admin routes independently enforce IP allowlisting, TOTP, an HttpOnly session cookie, and same-origin mutation checks — front-end navigation visibility is a convenience, not the security boundary.
12 Deployment Verification
Run through this checklist after every backend deployment, before pointing a mobile build at it:
GET /api/health→ HTTP 200 with anx-request-idheader (liveness only, no dependency checks).GET /api/ready→ HTTP 200 with MongoDB, Redis and QStash allok: true.GET /api/deployment/verifywithAuthorization: Bearer <DEPLOYMENT_CHECK_TOKEN>→ HTTP 200, every configuration flagtrue.- Trigger
GET /api/cron/minutelywithAuthorization: Bearer <CRON_SECRET>→ HTTP 202, three queued topics. - Confirm QStash delivers to
POST /api/workers, returns HTTP 202, and your logs show matchingjobId/requestIdvalues. - Confirm unauthenticated calls to the cron, worker and deployment-verification endpoints return HTTP 401.
Runtime logs are single-line JSON with timestamp, level, service and event; keys that look like tokens, secrets, authorization headers, signatures, passwords or private keys are redacted recursively before logging.
13 Known Limitations
Documented honestly so you can plan around them before a production rollout:
- TRON gasless delegation is gating-only.
POST /api/v1/tron/delegatevalidates address ownership and daily energy caps and returns202, but does not yet perform an on-chain stake+delegate transaction — the chain-adapter for that transaction type is not built. Do not surface a "gasless send succeeded" UI state from this response alone. - Third-party data providers are verified against realistic mocks, not live traffic, in the environment this was built in. Confirm behavior against your own real
MORALIS_API_KEY,TRONGRID_API_KEY,HELIUS_API_KEYetc. before depending on it in production. - EVM networks are reported separately, never merged. The same asset held on Ethereum, Base and BNB Chain returns as three separate balance/history entries — by design, since each network keeps its own address ownership and signing context even though the underlying key is shared.
14 Support
For questions about this setup guide, the native signer module, or custom development, contact the seller through the channel listed on the CodeCanyon item page.