Peer-to-peer inter-state ride sharing and parcel delivery for Nigeria. A Nimiq Pay Mini App — split the cost of trips already happening, with payment held in escrow until arrival.
# NimRide NG
Peer-to-peer inter-state ride sharing and package delivery for Nigeria, built as a
**Nimiq Pay Mini App**. Riders share the cost of trips they were already making;
traders move parcels down corridors that already have vehicles on them. Money is held
in escrow instead of changing hands at the motor park.
Popular corridors modelled: Lagos–Abuja, Lagos–Port Harcourt, Abuja–Kano, Lagos–Ibadan,
Abuja–Kaduna, Enugu–Lagos.
---
## Stack
| Concern | Choice |
| --- | --- |
| Build | Vite 6 + React 18 + TypeScript (strict) |
| Styling | Tailwind CSS 3, dark-first, `#008751` green + gold accents |
| Primitives | Radix UI (`dialog`, `tabs`) + `cva` variants |
| State | Zustand (`useWalletStore`, `useAppStore`) |
| Wallet | `@nimiq/mini-app-sdk` v0.1.0 |
| Routing | React Router (**HashRouter** — see below) |
Production bundle: **~119 kB gzipped** total, no runtime network calls for UI.
---
## Getting started
```bash
npm install
```
```bash
npm run dev
```
```bash
npm run build
```
`dist/` is fully static and uses relative asset paths (`base: './'`), so it can be
dropped on any static host or served from a subpath.
---
## Nimiq integration
All wallet communication is owned by `src/lib/nimiq/client.ts`. No component ever
touches `window.nimiq` or `window.ethereum` directly. The single consumer-facing
entry point is the `useNimiq()` hook.
### Two settlement rails
The app deliberately treats these differently, because they have different levels of
verification behind them:
**1. NIM — primary, verified.** `init()` from `@nimiq/mini-app-sdk` resolves a
`NimiqProvider`. Three things about this API bite if you assume EVM conventions:
- Amounts are **Lunas**, not NIM. `1 NIM = 100,000 Lunas`, and `value` is a JS `number`,
so `nimToLunas()` converts via `BigInt` and guards `MAX_SAFE_INTEGER`.
- Addresses are IBAN-style `NQ…` blocks, not `0x…`.
- **Errors resolve, they do not reject.** Failures come back as `{ error: { type, message } }`.
Every provider call …