Skip to content

Settle-Official/settle-official

Repository files navigation

Settle

Settle is a Next.js app that bridges fiat and Stellar USDC in both directions:

  • Offramp — Stellar USDC → Base (via Allbridge) → fiat payout (via Paycrest)
  • Onramp — fiat (via Paycrest) → custodial Base hot wallet → Stellar USDC (via Allbridge), delivered to the user's Stellar address

Server-side state (order status, bridge progress, platform stats) is backed by Upstash Redis; the browser only keeps a local transaction-history cache.

Tech Stack

  • Next.js 15 (App Router)
  • React 19 + TypeScript
  • Tailwind CSS v4
  • Stellar SDK + Freighter API (wallet signing)
  • Allbridge Bridge Core SDK (Base ⇄ Stellar bridging)
  • viem (Base chain transfers, custodial hot wallet)
  • Upstash Redis (order/status stores)
  • Telegram Bot API (operational alerts + manual retry commands)

Features

Offramp (Stellar USDC → fiat)

  • Connect Stellar wallet (Freighter auto-detect first, Lobstr/WalletConnect fallback)
  • Get a USDC → fiat quote, verify recipient bank account
  • Build and sign the Stellar → Base bridge transaction (XDR)
  • Submit to Stellar Horizon, poll bridge status
  • Execute payout server-side (Base USDC transfer + Paycrest order)
  • Poll payout status via SSE stream

Onramp (fiat → Stellar USDC)

  • User pays fiat into a Paycrest-issued virtual account
  • Paycrest settles USDC into the platform's custodial Base hot wallet (Paycrest has no native Stellar support)
  • Server bridges Base → Stellar to the user's address once settled
  • Destination defaults to the connected wallet, but the user can opt into a different Stellar address via a checkbox in the form
  • Hold-and-alert on failure: if the bridge can't complete (e.g. hot wallet out of Base ETH for gas), funds stay put, the order is marked bridge_failed, and a Telegram alert asks for manual resolution — nothing is ever auto-refunded or silently retried
  • A backstop cron (/api/cron/finalize-onramp) and the open SSE session both watch in-flight bridges and flip orders to delivered once Allbridge confirms on Stellar

Operations (Telegram)

  • Rich per-transaction alerts for every offramp/onramp status change (bank details, amount, rate, payout, Stellar address)
  • Critical alerts for anything requiring manual action (e.g. bridge_failed)
  • A stuck onramp bridge can be retried directly from Telegram — no admin app needed — by sending /retry <orderId> [amount] to the configured chat (see Manual Bridge Retry below)

Getting Started

1. Install

npm install

2. Configure env

cp .env.example .env.local

Set values in .env.local:

Paycrest

  • PAYCREST_API_KEY
  • PAYCREST_WEBHOOK_SECRET

Offramp bridge (Stellar → Base)

  • BASE_PRIVATE_KEY
  • BASE_RETURN_ADDRESS / NEXT_PUBLIC_BASE_RETURN_ADDRESS
  • BASE_RPC_URL (optional, defaults to https://mainnet.base.org)
  • STELLAR_SOROBAN_RPC_URL / NEXT_PUBLIC_STELLAR_SOROBAN_RPC_URL (optional)
  • STELLAR_HORIZON_URL (optional)

Onramp bridge (Base → Stellar, custodial)

  • ONRAMP_HOT_WALLET_ADDRESS / ONRAMP_HOT_WALLET_PRIVATE_KEY — platform-operated Base wallet; Paycrest delivers onramp USDC here, so it must be funded with a little Base ETH for gas
  • ONRAMP_MIN_GAS_ETH (optional, default 0.0005) — refuses to bridge below this ETH balance rather than risk a stranded tx

Storage / stats

  • UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN
  • STATS_SEED_USERS / STATS_SEED_VOLUME (seed values shown before real activity accumulates)

Telegram alerts + admin

  • TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID — leave blank to disable alerts entirely
  • TELEGRAM_WEBHOOK_SECRET — verifies inbound Telegram commands (see below)
  • ADMIN_API_SECRET — protects the HTTP bridge-retry endpoint

Cron

  • CRON_SECRET — Vercel Cron sends this as a Bearer token to /api/cron/finalize-onramp

Wallet connect

  • NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID

3. Run

npm run dev

Open http://localhost:3000.

Manual Bridge Retry

If an onramp bridge is stuck — most commonly because the hot wallet ran out of Base ETH for gas — fund the hot wallet, then retry the bridge for that order id. Both paths share the same lock-guarded, idempotent logic (src/lib/onramp/retry-bridge.ts), so retrying a bridge already in flight is a safe no-op.

Via Telegram (register once per deployment):

curl -G "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \
  --data-urlencode "url=https://<your-deployment>/api/webhooks/telegram" \
  --data-urlencode "secret_token=$TELEGRAM_WEBHOOK_SECRET"

Then, in the configured chat:

/retry <orderId> [amount]

amount is only needed if the order never made it to settled in the store (so no USDC amount was persisted) — pass the settled USDC amount from the Paycrest webhook/dashboard in that case.

Via HTTP:

curl -X POST https://<your-deployment>/api/admin/onramp/retry-bridge \
  -H "Authorization: Bearer $ADMIN_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"orderId":"<orderId>","amount":"<optional override>"}'

API Routes

Offramp

  • POST /api/offramp/quote
  • GET /api/offramp/currencies
  • GET /api/offramp/institutions/[currency]
  • POST /api/offramp/verify-account
  • POST /api/offramp/paycrest/order — create the Paycrest payout order
  • GET /api/offramp/paycrest/order/[orderId]
  • POST /api/offramp/execute-payout
  • GET /api/offramp/status/[orderId]
  • GET /api/offramp/stream/[orderId] — SSE status stream

Offramp bridge (Stellar → Base)

  • POST /api/offramp/bridge/build-tx
  • GET /api/offramp/bridge/gas-fee-options
  • POST /api/offramp/bridge/submit-soroban
  • GET /api/offramp/bridge/status/[txHash]
  • GET /api/offramp/bridge/tx-status/[hash]

Onramp

  • POST /api/onramp/order — create the Paycrest onramp order; persists the user's destination Stellar address
  • GET /api/onramp/order/[orderId]
  • GET /api/onramp/stream/[orderId] — SSE status stream, also drives fast-path bridge finalization
  • GET /api/onramp/bridge/status/[txHash]

Webhooks

  • POST /api/webhooks/paycrest — routes onramp vs. offramp by looking up which store the order lives in (Paycrest's payload has no reliable direction field)
  • POST /api/webhooks/telegram — inbound /retry command handling

Cron

  • GET /api/cron/finalize-onramp — backstop sweep of in-flight onramp bridges

Admin

  • POST /api/admin/onramp/retry-bridge — manually re-trigger a stuck onramp bridge

Misc

  • GET/POST /api/stats — platform stats (seeded + accumulated)

Storage Model

  • Server (Upstash Redis) is the source of truth for anything that survives a page close or webhook retry:
    • Onramp order state + Base→Stellar bridge progress (onramp:order:*, onramp:pending-bridges)
    • Offramp payout status (payout:*) and order metadata for alert enrichment (paycrest:order-meta:*)
    • Platform stats (seeded values + real accumulated activity)
  • Browser localStorage (stellaramp_transactions, max 50 records, scoped by connected wallet) is a client-side history cache only — not authoritative.

Scripts

npm run dev
npm run build
npm run start
npm run lint

Notes

  • Onramp is custodial by design between fiat settlement and Stellar delivery: Paycrest has no native Stellar support, so USDC always transits the platform's Base hot wallet before being bridged onward. The hold-and-alert failure policy exists specifically to keep that window safe — funds are never auto-refunded or blindly retried.
  • Keep .env.local, .next, and node_modules out of version control (already covered by .gitignore).

About

Offramp your Stellar USDC to Fiat in Minutes.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors