skip to content
[··]nano-os v7.0 — booting portfolio
revival nano adolor
senior frontend engineer
00%
← work/03Finnainternal product▶ find it in play mode

Finna

Crypto-native money: wallets, swaps, lending.

Led the React Native app on a Redux Toolkit + TanStack Query architecture with per-domain service layers: a multi-currency wallet (crypto, NGN, USD), lending, P2P requests and bill payments.

role
Lead mobile engineer
year
2025 — 2026
platform
iOS · Android
stack
React NativeRedux ToolkitTanStack QueryWebAuthnSentry
Just-in-time swap — cover the shortfall from USDT or USDC
Loans — collateral balance and active loans
Home — total balance, quick actions and wallets
✓ passkey · WebAuthn
shortfall ₦35,000 → swap 25 USDT, then pay
3
currencies in one wallet
3
auth factors — PIN, TOTP, passkeys
JIT
swap that rescues short balances

$ ls ./screens

10 screens · scroll →
Home — total balance, quick actions and wallets
Home — total balance, quick actions and wallets
Short on naira — auto-convert offered inline
Short on naira — auto-convert offered inline
Just-in-time swap — cover the shortfall from USDT or USDC
Just-in-time swap — cover the shortfall from USDT or USDC
Withdrawal successful
Withdrawal successful
Loans — collateral balance and active loans
Loans — collateral balance and active loans
Get a loan — amount, collateral and duration
Get a loan — amount, collateral and duration
Loans — first-time empty state
Loans — first-time empty state
USDT wallet — receive, send, swap
USDT wallet — receive, send, swap
Home — naira and dollar accounts
Home — naira and dollar accounts
Bills — airtime, data, internet, TV, power, betting
Bills — airtime, data, internet, TV, power, betting

The problem

Finna is a crypto-native finance app: hold stablecoins, spend in naira, borrow against what you hold. The product is only as good as its edges — the moment a payment fails because the money sits in the wrong currency, users stop trusting the whole thing.

What I built

I led the React Native app alongside product, backend and QA, on a Redux Toolkit + TanStack Query architecture with per-domain service layers.

  • A multi-currency wallet — crypto, NGN and USD — with on/off-ramp, crypto send and receive, currency conversion and virtual bank accounts.
  • A just-in-time swap. If a payment would fail because the NGN balance is short but the user holds USDT, the app quotes and executes the conversion for just the shortfall, then completes the payment — one confirmation, not three screens.
  • Crypto-backed lending with a live loan-health view.
  • Recurring P2P payment requests, and merchant bill payments with real-time operator detection from the phone number.
  • Authentication in layers — transaction PIN, TOTP 2FA and WebAuthn passkeys.
the shape of a just-in-time swap
async function payBill(bill: Bill, amount: Naira) {
  const shortfall = amount - balances.NGN;
  if (shortfall > 0) {
    const quote = await swaps.quote({ from: "USDT", to: "NGN", amount: shortfall });
    await swaps.execute(quote, { idempotencyKey: bill.paymentId });
  }
  return bills.pay(bill, amount, { idempotencyKey: bill.paymentId });
}

Reusing the payment's idempotency key for the swap is the important line: retry the whole flow and you get the same swap, not a second one.

Outcome

  • Payments that used to fail on a currency mismatch now go through in a single confirmation.
  • Three authentication factors, with passkeys removing most PIN fatigue.
  • One architecture across wallet, lending and payments, so features land in a known shape.
next case study →TipanyTip anyone, get paid by link or QR
✓