Decentralized savings circles (ROSCAs) built on Stellar and Soroban.
Overview β’ Features β’ Quick Start β’ API β’ Smart Contract β’ Contributing β’ Roadmap
- Overview
- Features
- Architecture
- Tech Stack
- Quick Start
- Project Structure
- Smart Contract
- API Reference
- Testing
- Deployment
- Contributing
- Roadmap
- FAQ
- License
Stellar ROSCA is a decentralized implementation of the Rotating Savings and Credit Association β a group savings model where members contribute a fixed amount each cycle and one member receives the entire pool, rotating until everyone has been paid out.
Known as Chamas in Kenya, Susus in West Africa, and Tontines across the diaspora, ROSCAs are a vital financial tool for millions of people worldwide. Traditional ROSCAs rely on trust and physical proximity. This project puts the logic on-chain.
- π Trustless β contribution rules and payout logic enforced by a Soroban smart contract, not a person
- π¦ Multisig treasury β group funds held in a Stellar multisig account; no single member can drain it
- π Globally accessible β anyone with a Stellar wallet can participate
- β‘ Fast and cheap β Stellar settles in ~5 seconds at a fraction of a cent per transaction
- π Transparent β every contribution and payout is an immutable on-chain record
- β Create ROSCA groups with configurable members, contribution amount, and cycle length
- β Deterministic recipient queue shuffled at group creation
- β Per-cycle contribution tracking with duplicate prevention
- β Token escrow β funds held in contract until payout is triggered
- β Automated payout to next recipient when all members have contributed
- β Cycle advancement and group completion detection
- β On-chain events for every state change
- β Group creation with Stellar multisig treasury provisioning
- β Contribution submission β builds and signs Stellar payment transactions
- β All-contributed detection per cycle
- β RESTful JSON API with proper error codes
- π§ Group dashboard
- π§ Contribution tracking UI
- π§ Cycle progress visualization
- π§ Wallet integration
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Frontend (Next.js / React) β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β Group Card β βCycle Progressβ β Dashboard β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Backend API (Node.js / Express) β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β Group Routes β β Contribution β βStellar Clientβ β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Stellar Network (Soroban) β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β ROSCA Contract (Rust) β β
β β - create_group() - trigger_payout() β β
β β - contribute() - get_group_status() β β
β β - get_current_recipient() β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Member 1 Member 2 Member 3 Backend Contract
β β β β β
ββPOST /contributeβββββββββββββββΊ β β
β β β recordContribution() β
β β β ββtoken.transferβΊ
β β β ββContrib eventββ€
ββ{ txHash, allContributed: false }ββββββββββββββ€ β
β β β β β
β ββPOST /contributeβββββββββββββββΊ β
β β β ββtoken.transferβΊ
β β β ββContrib eventββ€
β ββ{ txHash, allContributed: false }ββββββββββββββ€
β β β β β
β β ββPOST /contributeβββββββββββββββΊ
β β β ββtoken.transferβΊ
β β β ββAllCtrb eventββ€
β β ββ{ txHash, allContributed: true }
β β β β β
β β β POST /payout β β
β β β ββtrigger_payoutβΊ
β β β β verify + pay β
β β β ββPayout eventβββ€
β β β β cycle++ β
| Technology | Version | Purpose |
|---|---|---|
| Rust | 2021 | Contract language |
| Soroban SDK | 20.0.0 | Stellar smart contract framework |
| Technology | Version | Purpose |
|---|---|---|
| Node.js | 18+ | Runtime |
| Express | 4.19 | HTTP framework |
| @stellar/stellar-sdk | 11.3 | Stellar network interaction |
| Jest | 29 | Testing |
| Technology | Version | Purpose |
|---|---|---|
| Next.js | 14 | React framework |
| React | 18 | UI |
| TailwindCSS | 3.4 | Styling |
Prerequisites: Node.js 18+ Β· Rust Β· Stellar CLI
git clone https://github.com/cybermax4200/stellar-rosca.git
cd stellar-rosca
cp backend/.env.example backend/.env
# Edit backend/.env β set STELLAR_NETWORK, HORIZON_URL, etc.cd backend && npm install && cd ..
cd frontend && npm install && cd ..cd backend && npm run devVerify it's running:
curl http://localhost:3000/health
# { "status": "ok", "network": "testnet" }cd frontend && npm run dev
# http://localhost:3001stellar-rosca/
βββ contracts/
β βββ rosca/
β βββ src/
β β βββ lib.rs # Contract logic β create_group, contribute, trigger_payout
β βββ Cargo.toml
βββ backend/
β βββ src/
β β βββ index.js # Express server entry point
β β βββ group.js # Group creation + in-memory store
β β βββ contribution.js # Contribution recording + Stellar tx submission
β β βββ stellar.js # Horizon client, multisig account helpers
β β βββ routes/
β β βββ groups.js # REST route handlers
β βββ tests/
β β βββ group.test.js
β β βββ contribution.test.js
β βββ package.json
βββ frontend/
β βββ components/ # React components
β βββ pages/ # Next.js pages
β βββ styles/
βββ docs/
β βββ architecture.md # Full system design + security model
β βββ api.md # Complete API reference
βββ .github/
β βββ ISSUE_TEMPLATE/
β βββ bug_report.md
β βββ feature_request.md
βββ CONTRIBUTING.md
The rosca Soroban contract is the on-chain source of truth. It handles authorization, token escrow, and payout logic. Written in Rust, compiled to WASM.
Creates a new ROSCA group and shuffles the recipient queue.
pub fn create_group(
env: Env,
admin: Address, // group administrator
token: Address, // token contract for contributions
members: Vec<Address>,
contribution_amount: i128,
cycle_duration_days: u32,
) -> u32 // returns group_idTransfers tokens from a member to the contract escrow and records the contribution.
pub fn contribute(
env: Env,
group_id: u32,
member: Address,
amount: i128,
)Verifies all members have contributed, transfers the full pot to the next recipient, and advances the cycle.
pub fn trigger_payout(env: Env, group_id: u32)Returns the address of the member scheduled to receive the payout this cycle.
pub fn get_current_recipient(env: Env, group_id: u32) -> AddressReturns the full group state.
pub fn get_group_status(env: Env, group_id: u32) -> Group| Code | Error | Description |
|---|---|---|
GroupNotFound |
Group does not exist in storage | |
NotAMember |
Caller is not a member of the group | |
AlreadyContributed |
Member has already contributed this cycle | |
NotAllContributed |
Payout attempted before all members contributed | |
GroupCompleted |
All cycles have finished |
| Topic | Data | Emitted when |
|---|---|---|
(GroupCrtd, group_id) |
(admin, group_id) |
Group created |
(Contrib, group_id) |
(member, cycle, amount) |
Member contributes |
(AllCtrb, group_id) |
(group_id, cycle) |
All members contributed |
(Payout, group_id) |
(recipient, cycle, amount) |
Payout sent |
# Build
cd contracts/rosca
stellar contract build
# Deploy to testnet
stellar contract deploy \
--wasm target/wasm32v1-none/release/rosca.wasm \
--source <YOUR_SECRET_KEY> \
--network testnetThe command prints the contract ID β save it in backend/.env as CONTRACT_ID.
Base URL: http://localhost:3000/api Β· All amounts in stroops (1 XLM = 10,000,000 stroops)
| Method | Endpoint | Status | Description |
|---|---|---|---|
GET |
/health |
β Live | Server health check |
POST |
/groups/create |
β Live | Create group + multisig treasury |
POST |
/groups/:id/contribute |
β Live | Submit member contribution |
GET |
/groups/:id/status |
π§ Stub | Get group state |
POST |
/groups/:id/payout |
π§ Stub | Trigger cycle payout |
GET |
/groups/:id/members |
π§ Stub | List group members |
Full docs with typed request/response bodies and curl examples: docs/api.md
Create a group
curl -X POST http://localhost:3000/api/groups/create \
-H "Content-Type: application/json" \
-d '{
"adminSecret": "SADMIN...",
"members": ["GMEMBER1...", "GMEMBER2...", "GMEMBER3..."],
"contributionAmount": 10000000,
"cycleDurationDays": 7
}'Contribute
curl -X POST http://localhost:3000/api/groups/<id>/contribute \
-H "Content-Type: application/json" \
-d '{ "memberSecret": "SMEMBER...", "amount": 10000000 }'# Backend β Jest with mocked Stellar SDK
cd backend && npm test
# Contract β Rust
cd contracts/rosca && cargo testCurrent: 8 passing tests across group creation and contribution flows.
See CONTRIBUTING.md for the full deploy walkthrough. Quick reference:
cd contracts/rosca
stellar contract build
stellar contract deploy \
--wasm target/wasm32v1-none/release/rosca.wasm \
--source <YOUR_SECRET_KEY> \
--network testnetFor frontend deployment, run npm run build inside /frontend and deploy the .next output to Vercel or any Node-compatible host.
Contributions are welcome. See CONTRIBUTING.md for full setup, test, and deploy instructions.
- Fork the repo
- Create a branch:
git checkout -b feature/your-feature - Make your changes and write tests
- Ensure everything passes:
npm test/cargo test - Commit using Conventional Commits:
feat: add your feature - Open a PR referencing the relevant issue:
Closes #42
| Type | Pattern |
|---|---|
| Feature | feature/short-description |
| Bug fix | fix/short-description |
| Docs | docs/short-description |
-
create_group,contribute,trigger_payout - Token escrow and multisig treasury
- On-chain events
- Group creation endpoint
- Contribution endpoint with Stellar tx submission
- Jest test suite with mocked SDK
- Group management UI
- Contribution tracking and cycle progress
- Wallet integration
- Replace in-memory store with PostgreSQL or MongoDB
- Transaction history and audit log
- Security audit
- Mainnet deployment
- Verifiable randomness for recipient queue (Pyth Entropy)
What is a ROSCA? A group savings model where members contribute a fixed amount each cycle and one member receives the full pool, rotating until everyone has been paid out.
Do I need to write code to use this? To run a group today you interact via the API or frontend. No Rust or Solidity knowledge needed.
Which network is supported? Stellar testnet for development. Mainnet deployment is planned for Phase 5 after a security audit.
What happens if a member doesn't contribute? The contract blocks the payout until all members have contributed. A deadline + slash mechanism is planned for Phase 4.
Why is the recipient queue not truly random? True randomness isn't available on-chain β every validator must reproduce the same result. The queue is shuffled using a deterministic LCG seeded from the ledger timestamp. A production deployment will use a verifiable randomness oracle.
Where are group funds held?
In the Soroban contract as escrow until trigger_payout is called. The multisig treasury account adds a second layer β no single member can move funds unilaterally.