Stellar-IndigoPay is an open-source climate donation platform built on the Stellar network. Donors give XLM (and USDC) directly to verified environmental projects — funds never touch a custodian. Every donation is recorded on-chain via a Soroban smart contract, so total impact, donor reputation, and CO₂ offsets are publicly auditable by anyone, in any language, on any device.
The same platform ships as:
| Surface | What it is | Built with |
|---|---|---|
| 🌐 Web app | Donor dashboard, project browse, leaderboard, AI impact summaries, multi-wallet | Next.js 14 · React · TypeScript · Tailwind |
| 📱 Mobile app | QR-scan-to-give, biometric auth, secure wallet storage, push receipts | React Native · Expo · expo-router |
| 🧩 Browser extension | Detect Stellar addresses on any page, donate in one click | Manifest V3 · Webpack (Chrome + Firefox) |
| ⛓️ 4 Soroban contracts | Donation ledger (136 codes), escrow (62), attestation (60), oracle (50) — 308 structured error codes | Rust · WASM wasm32v1-none |
| 🛠 Backend API | Metadata, leaderboard, webhooks, AI summaries, admin, event streaming | Node.js 22 · Express · Postgres · pg-boss |
- 🔐 Custody-minimised — XLM goes directly from donor wallet to project wallet. The platform never holds funds.
- 📜 On-chain transparency — Soroban is the single source of truth. Anyone can read
get_project(),get_donor_stats(),get_global_total()without trusting us. - 🪪 No accounts — your Stellar keypair is your identity. No email, no password, no recovery phone.
- 🏷 Reputation you own — Impact badges (🌱 Seedling, 🌳 Tree, 🌲 Forest, 🌍 Earth Guardian) and Impact NFTs are wallet-bound and travel with you across dApps.
- 💱 Multi-currency — Donate in XLM or USDC. USDC amounts are converted via a configurable on-chain price oracle.
- 🗳 Community governance — Badge holders vote to verify new projects using quadratic voting. On-chain proposals with configurable voting windows.
- 🤖 AI impact summaries — every project gets a plain-language explainer of where donations go, generated via Anthropic Claude and cached server-side.
- 🔔 Webhooks for partners — signed (HMAC-SHA256), retried (6-attempt backoff), dead-lettered milestone events.
- 🌉 Cross-chain attestation — verifiable on-chain records that a donation originated from another chain (Ethereum, Polygon, etc.).
- ⚡ Real-time events — SSE and Socket.IO streams for live donation ticker and Soroban contract event synchronization.
- 🛰 Production-grade ops — Helm, ArgoCD, Prometheus, Alertmanager with PagerDuty/Slack routing, monthly restore drills, SBOM + cosign signing.
A 3-minute motion-graphics pitch with a neural voice-over, walking through the problem (opaque, fee-heavy giving), the solution (direct, custody-free, on-chain donations), the product tour, the Soroban engineering depth, community traction, and the call to action.
📹 Watch the 3-minute pitch: Download MP4 (9.9 MB)
Script & tooling:
scripts/pitch-video-script.md· generated byscripts/pitch_video.py
🔗 stellar-indigo-pay.vercel.app
The frontend is deployed on Vercel and connected to Stellar Testnet. You can browse projects, connect your Freighter wallet (switched to Testnet), and make test donations immediately — no local setup required.
| Item | Value |
|---|---|
| 🌐 Live URL | stellar-indigo-pay.vercel.app |
| ⛓️ Network | Stellar Testnet |
| 🏭 Hosting | Vercel |
| 🔑 Wallet | Freighter (switch to Testnet) |
| 💰 Testnet XLM | Friendbot |
| 📦 Slim Contract ID (Testnet) | CAPE7IB3...INPE2 |
| 📦 Optimized Contract ID (Testnet) | CCG3QSD...J6BT |
You can be donating on testnet in under five minutes.
| Tool | Version | Why |
|---|---|---|
| Node.js | 22 LTS | Backend + frontend + mobile scripts |
| npm | 10+ | Package manager |
| Docker + Docker Compose | Latest | One-command dev environment |
| Freighter Wallet | Latest | Stellar browser wallet (or Freighter Mobile on phones) |
| (optional) Rust | 1.91+ | Only if you want to build the Soroban contracts |
git clone https://github.com/Stellar-IndigoPay/Stellar-IndigoPay.git
cd Stellar-IndigoPay
chmod +x scripts/setup-dev.sh
./scripts/setup-dev.shThe setup script installs Node deps for the backend, frontend, mobile, and extension and verifies the toolchain.
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build| Service | URL |
|---|---|
| 🖥 Frontend | http://localhost:3000 |
| 🛠 Backend API | http://localhost:4000 |
| 📜 Swagger UI | http://localhost:4000/api/docs |
| ❤️ Health | http://localhost:4000/api/health |
| 🗄 Postgres | localhost:5432 (indigopay / indigopay) |
| 📦 Redis (optional) | localhost:6379 |
The docker-compose.dev.yml override mounts source code into the containers and enables hot-reload for both Next.js and Express. Source edits refresh in seconds.
- Install Freighter and switch it to Testnet
- Copy your public key (starts with
G…) - Visit
https://friendbot.stellar.org?addr=<YOUR_PUBLIC_KEY>and you'll get 10 000 test XLM in a few seconds
- Open http://localhost:3000
- Click Connect Wallet → approve in Freighter
- Pick a project → enter an amount → sign the transaction in Freighter
- Refresh the dashboard — your donation is recorded both on-chain and in the backend
That's it. No account creation, no email verification, no KYC.
┌──────────────────────────────────┐
│ Donor (Freighter) │
└─────┬───────────────┬────────────┘
│ │
sign locally │ │ scan QR
▼ ▼
┌──────────────────────┐ ┌────────────────────┐
│ Web (Next.js) │ │ Mobile (Expo) │
│ Extension (MV3) │ │ │
└────────┬─────────────┘ └────────┬───────────┘
│ REST + WebSocket │
▼ ▼
┌─────────────────────────────────────────────┐
│ Node.js Backend (Express, Postgres) │
│ • Project metadata & leaderboard │
│ • Donation record (durable, idempotent) │
│ • Webhook delivery (pg-boss + DLQ) │
│ • AI impact summaries (Anthropic) │
│ • Admin + audit log │
│ • Sentry traces, Prometheus metrics │
└──┬──────────────────┬──────────────┬───────┘
│ │ │
▼ ▼ ▼
┌────────────────┐ ┌────────────┐ ┌──────────────┐
│ Postgres │ │ Redis │ │ Horizon / │
│ (durable │ │ (cache) │ │ Soroban RPC │
│ ledger) │ │ │ │ (indexer) │
└────────────────┘ └────────────┘ └──────┬───────┘
│
▼
┌──────────────────────────────┐
│ Soroban Contracts │
│ • IndigoPay (donation ledger)│
│ • Escrow (milestone payouts) │
│ • Attestation (cross-chain) │
│ • Oracle (price feed) │
│ Source of truth │
└──────────────────────────────┘
Key design choices (full rationale in docs/architecture.md):
- Direct-to-project payments — funds flow donor → project wallet. The contract records the event; it never custodies funds.
- Backend is optional — if the API is down, donations still succeed; you just can't see the leaderboard.
- Soroban is the source of truth — the contract exposes 20+ read functions; the backend is a queryable cache.
- Structured errors — all 4 contracts use
#[contracterror]enums (308 numeric codes total); no string-parsing needed for error handling. - Wallet-as-identity — auth is
require_auth()on the Stellar keypair. No password reset, no email enumeration. - Defense in depth — NetworkPolicies (default-deny),
PodDisruptionBudget,HorizontalPodAutoscaler, External Secrets, SBOM + Trivy + cosign, monthly restore drills.
- Browse verified projects with category, location, CO₂ offset, leaderboard rank
- Connect Freighter; sign donations locally — keys never leave the wallet
- Personal dashboard: lifetime donated, current badge, recent donations
- Project pages: campaign progress, milestones, ratings, AI-generated impact summary
- Real-time donation ticker + impact world map
- Internationalisation: English, French, Spanish (
frontend/lib/i18n.tsx) - Monthly giving setup with pause / cancel (
frontend/lib/monthlyGiving.ts) - Project comparison + wishlist + autocomplete
- Wallet address QR generator, project QR donation
- iOS + Android via a single React Native codebase
expo-routerfile-based navigation- Biometric auth for sensitive flows (
mobile/hooks/useBiometricAuth.ts) - Secure store for cached secrets (
mobile/lib/secureStore.ts) - QR donation with camera (
mobile/app/scan.tsx) - Deep links for mobile wallets (
freighter://tx?xdr=…) - Push notifications for donation receipts and project updates
- Offline support with AsyncStorage-backed cache
- Manifest V3 for Chrome and Firefox (
extension/manifest.json,extension/manifest.firefox.json) - Detects Stellar addresses (matching
^G[A-Z0-9]{55}$) on any web page - Click the popup to send a donation to the detected address
| Capability | Entry points |
|---|---|
| Project registry | register_project, batch_register_projects, update_project_co2_rate, pause_project, resume_project, deactivate_project, deactivate_all_projects |
| Donations | donate(XLM), donate_usdc(…) with on-chain price oracle; create_recurring / cancel_recurring for automated giving |
| Campaigns | create_campaign, extend_campaign_deadline, close_campaign — time-bound fundraising with goal tracking |
| Reputation & NFTs | get_donor_stats, get_badge, mint_impact_nft — tiered badges (🌱 Seedling → 🌍 Earth Guardian) |
| Governance | create_proposal, vote_verify_project, resolve_proposal — quadratic voting gated by ≥ Seedling badge |
| Escrow integration | setup_campaign_escrow, fund_escrow, release_escrow_milestone, claim_escrow_refund — cross-contract calls to the escrow contract |
| Attestation settlement | settle_attestation — cross-contract recording of cross-chain donations verified by the attestation contract |
| Emergency withdrawal | initiate_emergency_withdrawal, execute_emergency_withdrawal — timelocked multi-token batch withdrawal |
| Vesting | create_vesting_schedule, release_vesting, cancel_vesting — time-locked token distribution |
| Storage GC | cleanup_vesting_cancelled, cleanup_proposals — permissionless garbage collection for expired entries |
| Fees | set_platform_fee, set_treasury, set_fee_recipient — configurable fee splits |
| Impact verification | publish_impact_root, verify_impact_inclusion, get_impact_periods — Merkle Mountain Range proofs for off-chain impact data |
| ZK donations | set_zk_verification_key, donate_zk — privacy-preserving donations via zk-SNARK proofs (feature-gated) |
| Admin & upgrades | transfer_admin → accept_admin (2-step), pause_contract / unpause_contract, propose_upgrade → 48h timelock → execute_upgrade |
| Read (20+ functions) | get_project, get_global_total, get_global_stats, get_donation_count, get_donation_record, get_project_count, get_campaign, get_voter_list, and more |
| Contract | Path | Purpose |
|---|---|---|
| Escrow | contracts/escrow-contract/ |
Milestone-based fund release with multi-sig admin, dispute resolution, multi-token (XLM + USDC) support |
| Attestation | contracts/attestation-contract/ |
Cross-chain donation attestation bridge — records verifiable on-chain proofs that a donation originated on another chain |
| Oracle | contracts/oracle-contract/ |
On-chain price oracle for XLM/USDC conversion used by the IndigoPay contract's multi-currency donation flow |
All four contracts use #[contracterror] enums with unique numeric codes — no string-based panic!() messages. Clients and indexers can match on error codes without parsing panic strings.
| Contract | Error Enum | Codes | Categories |
|---|---|---|---|
| IndigoPay | ContractError + VerificationError |
136 | Init/admin, project, donation, campaign, token, attestation, escrow, governance, ZK, NFT, upgrade, emergency, refund, impact |
| Escrow | EscrowError |
62 | Init, job creation, amendment, release, oracle, disputes, refunds, claims, admin |
| Attestation | AttestationError |
60 | Init/admin, relayer, pause, validation, attestation lifecycle, upgrade, aggregation |
| Oracle | OracleError |
50 | Init/admin, staking, price reporting, config, source oracles, aggregation |
| Total | 308 |
Full details: contracts/indigopay-contract/README.md · contracts/indigopay-contract/SECURITY.md · contracts/indigopay-contract/UPGRADE.md
- Express + Node 22 + zod env validation
- Postgres for durable storage (donations, profiles, projects, jobs, ratings, updates, subscriptions, webhooks, AI summaries)
- pg-boss for durable background jobs (webhook delivery, AI summaries, profile enrichment, digests)
- Webhook delivery:
webhookQueueworker with 6-attempt backoff (30s → 2m → 10m → 30m → 2h → 6h), DLQ, GitHub-stylet=…,v1=…HMAC-SHA256 signing, 5-min replay window, idempotency by event id (docs/webhook-receiver.md) - OpenAPI 3.0.3 spec served as Swagger UI at
/api/docs(docs/api/openapi.yaml) - Sentry error tracking + Prometheus metrics (
/metrics, bearer-token auth in prod) - Socket.IO for real-time donation ticker; SSE for Soroban contract event streaming
- Admin console with JWT + refresh tokens, audit log, project status changes
- zod-validated request payloads, express-rate-limit + csurf
- Pino structured logging,
X-Request-Idcorrelation on every request - 95% → 99.5% coverage thresholds across backend, frontend, mobile, and extension
- Sentry + Prometheus + webhook + indexer graceful shutdown wired through a lifecycle service
- Prometheus scrapes backend, indexer, and pg-boss job metrics
- Grafana dashboards with platform health, donation flow, AI cost, webhook health
- Alertmanager with PagerDuty + Slack + business-hours routing and inhibition rules
- Alert rules: 5xx rate, p99 latency, DB pool wait, slow query p99, readiness failing,
BackupMissed,RestoreDrillFailed - Docker Compose stack (
monitoring/docker-compose.monitoring.yml) and Helm chart integration
- Default-deny NetworkPolicy in the
indigopaynamespace, with explicit allow rules - HPA (min 2, max 10) + PDB (
minAvailable: 1) on backend and frontend - External Secrets operator template (
k8s/external-secret.yaml,docs/external-secrets.md) - SBOM on every push, Trivy image scan (informational), cosign keyless signing on release tags
- Gitleaks secret scan with a curated allowlist (
.gitleaks.toml) - Rate limit + CSRF + helmet + CSP + Sentry error capture
- Audit log of every admin action with actor, target, IP, and metadata
All four Soroban contracts are compiled with aggressive size optimization (opt-level = "z", lto = true, codegen-units = 1, strip = true, panic = "abort") and enforced by a 64 KB WASM size limit in CI. Key savings:
| Strategy | Impact |
|---|---|
| Feature gating (16 Cargo features) | Slim deployment at 51 KB vs 103 KB full; choose only what you need |
Shortened event symbols (symbol_short!) |
33% smaller XDR encoding (8-9 bytes avg vs 12-15) |
Bundled reads (get_global_stats) |
75% fewer RPC calls (1 vs 4) for dashboard hero |
| Instance storage for hot counters | Cheaper reads than persistent storage for admin threshold, global totals, pause flags |
Storage garbage collection (cleanup_*) |
Permissionless cleanup prevents TTL bloat and long-term storage cost growth |
| CEI pattern (Checks-Effects-Interactions) | Re-entrancy protection with zero gas overhead |
Per-operation benchmarks are documented for all 4 contracts — from donate (~10,000 stroops) to get_global_stats (~100 stroops) — with cross-contract flow estimates and before/after comparisons. See the full report in docs/gas-optimization.md.
- Nightly
pg_dumpto S3 / GCS, 30-day retention (.github/workflows/database-backup.yml) - Monthly restore drill that spins up an ephemeral Postgres and asserts row counts (
.github/workflows/restore-drill.yml) - Documented RTO / RPO, failure modes, and secret-compromise procedure (
docs/disaster-recovery.md,docs/restore-runbook.md)
2,400+ tests across 175+ files — 586 Soroban contract tests (unit + property-based fuzz with 10,000+ iterations), 1,069 backend tests (99 suites, 99.5% coverage target), 444 frontend tests (52 suites, 99.5% coverage target), 148 extension tests (6 suites, 99.5% coverage target), 239 mobile tests (27 suites, 99.5% coverage target).
| Layer | Command | Notes |
|---|---|---|
| Backend unit | cd backend && npm test |
Jest + supertest, in-memory Postgres via testcontainers |
| Frontend unit | cd frontend && npm test |
Jest + Testing Library |
| Frontend e2e | cd frontend && npm run test:e2e |
Playwright; accessibility checks via @axe-core/playwright |
| Contracts | cargo test --features testutils |
Rust unit + property-based fuzz (10 000+ iterations) |
| Contracts build | cargo build --workspace --target wasm32v1-none --release |
WASM artefacts in target/ |
| DAST | .github/workflows/ci.yml (ZAP baseline) |
OWASP ZAP baseline against the running frontend |
| Load | k6 run scripts/load-test.js |
See SLOs in docs/performance.md |
| Restore drill | .github/workflows/restore-drill.yml |
Monthly in CI |
| Environment | Path |
|---|---|
| 🌐 Vercel (live) | stellar-indigo-pay.vercel.app — production frontend on Stellar Testnet |
| Kubernetes (raw YAML) | k8s/ — namespace, configmap, secret, postgres, backend, frontend, ingress, HPA, PDB, NetworkPolicies, ExternalSecret |
| Helm chart | helm/indigopay/ — chart-driven reconciliation, tested in CI with helm lint + helm template |
| GitOps | gitops/argocd-application.yaml + gitops/argo-rollouts-canary.yaml for progressive delivery with Prometheus success-rate analysis |
| Local dev | docker compose -f docker-compose.yml -f docker-compose.dev.yml up |
| CI test | docker compose -f docker-compose.test.yml up |
| Mainnet launch | docs/deployment-mainnet.md |
Deploy the IndigoPay contract to Stellar Testnet:
chmod +x scripts/deploy-contract.sh
./scripts/deploy-contract.sh testnet aliceThe script outputs the deployed CONTRACT_ID. Set it in your .env files:
# frontend/.env.local
NEXT_PUBLIC_CONTRACT_ID=CCG3QSD7FWTZ5W7NG2N7UDYWYVXF3I2NY5JGT3QPTZ6KHOIKUHMMJ6BT
# backend/.env
CONTRACT_ID=CCG3QSD7FWTZ5W7NG2N7UDYWYVXF3I2NY5JGT3QPTZ6KHOIKUHMMJ6BTDeployed Testnet contract IDs:
Detail Slim Contract Optimized Contract Contract ID CAPE7IB3...INPE2CCG3QSD...J6BTFeatures Core registry + reads donation + campaign + full feature set WASM Size 51 KB (slim) 103 KB (wasm-opt -Oz) Deploy TX 70ec8c68...17af1801...Init TX 63eb9a72...8d5bb1b9...Contract Interaction TX register_project8db770da...register_projectde40c0ab...Donation TX — b577a3b4...Explorer View View
import { Contract, scValToNative, Address } from "@stellar/stellar-sdk";
const CONTRACT_ID = "CCG3QSD7FWTZ5W7NG2N7UDYWYVXF3I2NY5JGT3QPTZ6KHOIKUHMMJ6BT";
const contract = new Contract(CONTRACT_ID);
// Read global donation total (free, simulated call)
const result = await contract.call("get_global_total");
console.log("Total XLM raised:", scValToNative(result.retval));
// Read a registered project
const project = await contract.call("get_project",
scValToNative({ project_id: "project-001" }));
console.log("Project:", scValToNative(project.retval));
// Read project count
const count = await contract.call("get_project_count");
console.log("Projects registered:", scValToNative(count.retval));# Read a project
stellar contract invoke \
--id CCG3QSD7FWTZ5W7NG2N7UDYWYVXF3I2NY5JGT3QPTZ6KHOIKUHMMJ6BT \
--source deployer --network testnet \
-- get_project --project_id project-001
# Register a new project (requires admin key)
stellar contract invoke \
--id CCG3QSD7FWTZ5W7NG2N7UDYWYVXF3I2NY5JGT3QPTZ6KHOIKUHMMJ6BT \
--source deployer --network testnet \
-- register_project \
--admin GCRTWQ6NCS6XZPPYATVLZYLY5BBRGMA3J5VTQNTICQL4TZLXHZTEGAXC \
--project_id project-002 \
--name 'Solar Kenya Initiative' \
--wallet GCRTWQ6NCS6XZPPYATVLZYLY5BBRGMA3J5VTQNTICQL4TZLXHZTEGAXC \
--co2_per_xlm 6200
# Get global stats
stellar contract invoke \
--id CCG3QSD7FWTZ5W7NG2N7UDYWYVXF3I2NY5JGT3QPTZ6KHOIKUHMMJ6BT \
--source deployer --network testnet \
-- get_global_statsSee docs/contract-integration.md for the full partner SDK guide with TypeScript, Go, and Python examples.
Container images are multi-stage (builder + runner), pinned to node:22-alpine, built with npm ci --omit=dev, and signed with cosign on release tags.
The full doc tree is indexed in docs/README.md. Highlights:
docs/architecture.md— system overview, donation flow, design decisionsdocs/getting-started.md— five-minute first rundocs/gas-optimization.md— gas benchmarks & optimization strategies for all 4 contractsdocs/contract-integration.md— partner SDK guide with TypeScript + Go + Python examplesdocs/webhook-receiver.md— receiver guide for milestone eventsdocs/performance.md— SLOs and k6 recipesdocs/DEPLOYMENT.mdanddocs/deployment-mainnet.mddocs/disaster-recovery.mdanddocs/restore-runbook.mddocs/external-secrets.mddocs/extension-build-process.mddocs/zap-triage.md— DAST results workflowdocs/backend/— auto-generated TypeDoc site for the backend service layer — runnpm run docsinbackend/to generate (not committed to the repo)- ADRs — Stellar/Soroban vs EVM, direct-to-wallet vs custody, wallet-as-identity, CEI pattern
We welcome contributions of any size. See CONTRIBUTING.md for the full guide, including Freighter setup, Friendbot funding, Docker hot-reload, the k6 perf gate, wallet integration guidelines, and the changelog policy.
Quick checklist for a good PR:
- Tests pass locally (
npm testin the affected package) - Coverage meets the 99.5% threshold (
npm test -- --coverage) - Lint passes (
npm run lint) - Type-check passes (
npm run type-checkfor frontend / mobile) - For backend API changes, the OpenAPI spec is updated and Swagger UI reflects it
- For contract changes,
cargo test --features testutilspasses and an entry is added tocontracts/EVENTS.mdfor any new event - For new contract errors, add a unique variant to the
#[contracterror]enum - CHANGELOG.md has a one-line entry under
[Unreleased]in Keep-a-Changelog format - No secrets in the diff (CI runs gitleaks)
This project is governed by the Contributor Covenant.
If you find a vulnerability, please do not open a public issue. Use GitHub Security Advisories or contact the maintainers privately. See SECURITY.md for the response SLA (acknowledgement within 48h, patch within 30d for critical issues).
| Release | Highlights | Status |
|---|---|---|
| v1.0 | Wallet connect, project browse, donations, leaderboard, Soroban ledger | ✅ Shipped |
| v1.1 | Docker Compose, CI across all layers, unit + e2e tests | ✅ Shipped |
| v1.2 | Verified projects: admin review, on-chain registration | ✅ Shipped |
| v1.3 | Impact NFT badges (Seedling / Tree / Forest / Earth Guardian) | ✅ Shipped |
| v1.4 | Community features (follow, comments, monthly digests, impact dashboard) | ✅ Shipped |
| v1.5 | Impact dashboard: global map, real-time donation stream, project completion | ✅ Shipped |
| v2.0 | Multi-currency: USDC alongside XLM with on-chain price oracle | ✅ Shipped |
| v2.1 | DAO governance: badge-weighted voting on project verification, escrow contracts | ✅ Shipped |
| v2.2 | Cross-chain attestation bridge, DEX integration, campaign-escrow integration, storage garbage collection | ✅ Shipped |
| v2.2.1 | Structured error codes (308 across all contracts), 95% coverage targets, backend test expansion (99 suites / 1,069 tests) | ✅ Shipped |
| v2.3 | (Planned) Mainnet launch, mobile push notifications, advanced analytics, grant applications | 🚧 Planned |
Full backlog: ROADMAP.md.
MIT © the Stellar IndigoPay contributors.
- Stellar Development Foundation for Soroban and Horizon
- Freighter for the wallet that makes this UX possible
- The Soroban community for the smart-contract primitives
- Anthropic for the AI model that powers impact summaries
- Every donor, project owner, and contributor who has made this platform what it is
🌱 Built with care by an open community. Every commit matters.







