Skip to content

0xdevrel/remiq

Repository files navigation

Remiq

Remiq is a stablecoin-powered remittance app built on Solana. Users load USDC via credit/debit card, send USDC to existing Remiq users by email, and cash out to a bank account through a service wallet workflow.


Feature Status

Feature Status Notes
Email OTP auth Live Powered by Privy
Embedded Solana wallet Live Auto-created on login
Live USDC balance Live Helius devnet RPC, refreshes every 15s
Receive (QR code) Live Displays wallet address + QR
Send by email Live Sponsored P2P USDC transfer to existing Remiq/Privy users
Load via card Live Dodo Payments test environment (1-150 USDC top-ups)
Cash out to bank Live On-chain USDC → service wallet, 1-day processing

Tech Stack

Layer Technology
Framework Next.js 16 (App Router), TypeScript
Styling Tailwind CSS v4
Auth + Wallets Privy v3 (@privy-io/react-auth)
Blockchain Solana devnet via @solana/web3.js v1
Token transfers @solana/spl-token
RPC provider Helius
Payments (card) Dodo Payments
QR codes qrcode.react
Base58 encoding bs58

Project Structure

remiq/
├── src/
│   ├── app/
│   │   ├── page.tsx                  # Landing page
│   │   ├── layout.tsx                # Root layout with Privy provider
│   │   ├── globals.css               # Global styles, design tokens, animations
│   │   ├── login/
│   │   │   └── page.tsx              # Email OTP login/signup
│   │   ├── dashboard/
│   │   │   └── page.tsx              # Main app: balance, send, receive, load, cash out
│   │   └── api/
│   │       ├── payments/             # Load, webhook, activity, and cash-out routes
│   │       ├── transfers/            # Sponsored send-by-email transfer route
│   │       └── users/                # Recipient lookup and bank profile routes
│   └── components/
│       └── PrivyWrapper.tsx          # Privy provider config (Solana, embedded wallets)
├── .env.local                        # Secret keys (not committed)
├── package.json
├── supabase-send-by-email.sql         # Supabase tables for users, transfers, and bank profiles
└── README.md

Environment Variables

Create a .env.local file in the project root with the following:

Variable Description Source
NEXT_PUBLIC_PRIVY_APP_ID Privy app ID (public) dashboard.privy.io
PRIVY_APP_SECRET Privy app secret (server-only) dashboard.privy.io
NEXT_PUBLIC_HELIUS_API_KEY Helius RPC API key dashboard.helius.dev
DODO_PAYMENTS_API_KEY Dodo Payments secret key (server-only) dashboard.dodopayments.com
DODO_PRODUCT_ID Dodo one-time Pay What You Want top-up product ID Create a PWYW product with $1 min and $150 max
NEXT_PUBLIC_SERVICE_WALLET_ADDRESS Solana wallet that receives cash-out funds Your service wallet
SERVICE_WALLET_PRIVATE_KEY Server-only Solana service wallet secret key, JSON array or base58 Your devnet service wallet
DODO_PAYMENTS_WEBHOOK_KEY Dodo webhook signing secret Dodo webhook settings
APP_BASE_URL Public app URL used for Dodo checkout return URLs Vercel/project domain
SUPABASE_URL Supabase project URL for the server ledger Supabase project settings
SUPABASE_SERVICE_ROLE_KEY Server-only Supabase service role key for ledger writes Supabase API settings

Key User Flows

Authentication

  1. User enters email → receives OTP → verified by Privy
  2. On first login, Privy auto-creates an embedded Solana wallet
  3. Authenticated users are redirected to /dashboard

Live USDC Balance

  • On dashboard load, the app calls Helius devnet RPC (getTokenAccountsByOwner) with the Circle USDC devnet mint (4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU)
  • Balance sums all matching USDC token accounts and converts raw token units (6 decimals) to a human-readable USDC amount
  • Refreshes every 15 seconds automatically, with a manual refresh button and optimistic updates after successful loads, sends, and cash-outs

Load Wallet (Card)

  1. User clicks "Load Balance" in the dashboard
  2. Frontend calls POST /api/payments/load with the user's email, Solana wallet address, and selected amount
  3. Server validates a 1-150 USDC amount, creates a Supabase load intent, and creates a Dodo checkout with that dynamic top-up amount
  4. User is redirected to the Dodo hosted checkout page
  5. After payment, Dodo redirects back to /dashboard?load=pending&load_id=...
  6. Dodo sends a verified payment.succeeded webhook to /api/payments/webhook
  7. The backend idempotently sends devnet USDC from the service wallet to the user's Privy wallet
  8. Dashboard polls load status and refreshes balance once the Solana transfer is confirmed

Note: Configure DODO_PRODUCT_ID as a one-time Pay What You Want top-up product with $1 minimum and $150 maximum. Remiq passes the selected top-up amount to Dodo in cents, with quantity fixed at 1.

Cash Out to Bank

  1. Bank details step — User enters account holder name, bank name, account number, and IFSC code. Details are stored per Privy user in Supabase and pre-filled on return.
  2. Amount step — User enters USDC amount. Live balance is shown. Client and server validation reject zero, negative, fractional micro-unit, and unsafe amounts.
  3. On-chain transfer — Server prepares a sponsored Solana SPL token transfer transaction:
    • Creates associated token account for service wallet (idempotent)
    • Transfers specified USDC amount from user wallet to service wallet
    • Uses the service wallet as fee payer, so the user only needs USDC for the cash-out amount
    • User signs via Privy's useSignTransaction
    • Server adds the service wallet signature, broadcasts, and confirms on devnet
  4. Backend ledger — Server verifies the signed transaction amount, source, destination, fee payer, and memo before storing the cash-out, bank details, and payout status in Supabase
  5. Intent binding — Cash-out transactions include a Remiq memo with the server-created cash-out ID, so public Solana signatures cannot be replayed with different bank details
  6. Confirmation — User sees "Payment will be processed within 1 working day" message

Recent Activity

  • Dashboard calls /api/payments/activity?walletAddress=...
  • Activity is scoped to the current embedded wallet only
  • Shows wallet-specific balance loads, cash-outs, sent transfers, and received transfers from Supabase
  • Bank account details are never returned to the dashboard activity feed

Send Money

  1. User enters a recipient email and amount
  2. Server resolves the recipient through Privy and requires an existing embedded Solana wallet
  3. Server prepares a sponsored P2P USDC transfer from sender wallet to recipient wallet with a Remiq memo
  4. User signs via Privy's useSignTransaction
  5. Server verifies recipient, amount, sender, fee payer, and memo before broadcasting
  6. Supabase records the transfer so sender and recipient both see it in Recent Activity

Design System

Defined in src/app/globals.css via Tailwind CSS v4 @theme:

Token Value Usage
--color-background #0A0A0A Page background (onyx black)
--color-foreground #ffffff Primary text
--color-cyber-yellow #FDE047 Brand accent, CTAs
--color-onyx #0A0A0A Deep background
--color-charcoal #171717 Card backgrounds
--color-deep-gray #262626 Input backgrounds, borders

Custom animations: animate-float, animate-ticker, animate-pulse-dot, animate-flow-1/2/3


Local Development

# Install dependencies
npm install --legacy-peer-deps

# Start dev server
npm run dev

# Type-check
npx tsc --noEmit

# Build for production
npm run build

Open http://localhost:3000 in your browser.


External Service Setup

Privy

  • Create an app at dashboard.privy.io
  • Enable Email OTP login
  • Enable Solana embedded wallets
  • Set allowed domains to include localhost:3000 and your production domain
  • Copy App ID → NEXT_PUBLIC_PRIVY_APP_ID and App Secret → PRIVY_APP_SECRET

Helius

  • Create a free account at dashboard.helius.dev
  • Create an API key that can be used for devnet and mainnet RPC calls
  • Copy it → NEXT_PUBLIC_HELIUS_API_KEY

Dodo Payments

  • Create an account at dashboard.dodopayments.com
  • Use the test environment for development
  • Create a Pay What You Want one-time product with a $1-$150 range for top-ups
  • Copy the product ID → DODO_PRODUCT_ID
  • Generate an API key → DODO_PAYMENTS_API_KEY
  • Add http://localhost:3000 to allowed redirect URLs
  • Set APP_BASE_URL to the deployed app URL before using deployed webhooks
  • Add a webhook endpoint for POST /api/payments/webhook
  • Subscribe to payment.succeeded
  • Copy the webhook signing secret → DODO_PAYMENTS_WEBHOOK_KEY

Service Wallet

  • Fund the service wallet with devnet SOL for fees
  • Fund the service wallet's devnet USDC ATA with enough USDC to cover loads
  • To test mainnet mode, fund the same service wallet public key on mainnet with SOL for fees and PUSD for liquidity
  • Set NEXT_PUBLIC_SERVICE_WALLET_ADDRESS to the public key
  • Set SERVICE_WALLET_PRIVATE_KEY to the server-only secret key as either a JSON number array or base58 string
  • Do not expose SERVICE_WALLET_PRIVATE_KEY in any NEXT_PUBLIC_* variable

Supabase Ledger

  • Remiq stores load intents, processed webhook IDs, cash-out intents, cash-out records, user profiles, bank profiles, transfer intents, and transfer records in Supabase
  • Server routes use SUPABASE_SERVICE_ROLE_KEY; never expose it with a NEXT_PUBLIC_ prefix
  • Row Level Security should stay enabled with deny-by-default policies for anon and authenticated
  • Cash-out bank details are stored per Privy user in Supabase for payout processing; encrypt or tokenize them before production launch
  • Run supabase-send-by-email.sql in the Supabase SQL editor before using Send by Email or saved bank profiles
  • Run supabase-network-asset-migration.sql before using the Dashboard network toggle so devnet USDC and mainnet PUSD activity stay separated

Send by Email

  • Privy remains the identity source of truth; set PRIVY_APP_SECRET server-side so API routes can resolve users by email
  • Remiq resolves recipients with Privy, caches normalized email and embedded Solana wallet in user_profiles, then creates transfer intents and records in Supabase
  • Sends are sponsored stablecoin transfers: devnet uses USDC and mainnet uses Palm USD (PUSD). The server prepares the transaction with the service wallet as fee payer, the sender signs with Privy, and the server verifies recipient, amount, memo, mint, and sender before broadcasting
  • MVP sends only to existing Remiq/Privy users with embedded Solana wallets

Network Toggle

  • The Dashboard can switch between devnet USDC and mainnet PUSD
  • Devnet uses https://devnet.helius-rpc.com/?api-key=... and USDC mint 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU
  • Mainnet uses https://mainnet.helius-rpc.com/?api-key=... and Palm USD mint CZzgUBvxaMLwMhVSLgqJn3npmxoTo6nzMNQPAnwtHF3s
  • Mainnet balance, send, and cash-out operate on PUSD. Card loading remains disabled on mainnet until Dodo Payments production is integrated

Production Readiness

Before going live, the following changes are required:

  • Switch Solana network from devnet to mainnet-beta
  • Add Dashboard toggle for devnet USDC and mainnet Palm USD (PUSD)
  • Add Helius mainnet RPC support via https://mainnet.helius-rpc.com/?api-key=...
  • Add mainnet PUSD mint (CZzgUBvxaMLwMhVSLgqJn3npmxoTo6nzMNQPAnwtHF3s)
  • Switch Dodo Payments to live environment and update API key
  • Add server-side verification of Dodo webhook events before crediting accounts
  • Replace local SQLite load ledger with a production database
  • Encrypt or tokenize stored bank details before production
  • Implement actual fiat-to-recipient bank payout logic in the service wallet backend
  • Rate-limit and authenticate public mutation endpoints
  • Add proper error tracking (e.g. Sentry)

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors