Skip to content

Commit 685469c

Browse files
committed
feat: initialize SoroSave monorepo — decentralized group savings protocol
Set up the full project structure with: - Soroban smart contract (Rust) for rotating savings groups with group lifecycle, contribution tracking, payout distribution, admin controls, and dispute resolution - TypeScript SDK wrapping the contract with typed client methods - Next.js frontend with Freighter wallet integration, group browsing, creation, and contribution UI - CI/CD workflow, issue templates, and comprehensive documentation EOF )
0 parents  commit 685469c

51 files changed

Lines changed: 3648 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
name: Bug Report
3+
about: Report a bug in SoroSave
4+
title: '[Bug] '
5+
labels: bug
6+
assignees: ''
7+
---
8+
9+
## Description
10+
A clear description of the bug.
11+
12+
## Steps to Reproduce
13+
1. Go to '...'
14+
2. Click on '...'
15+
3. See error
16+
17+
## Expected Behavior
18+
What you expected to happen.
19+
20+
## Actual Behavior
21+
What actually happened.
22+
23+
## Environment
24+
- OS: [e.g., macOS, Ubuntu]
25+
- Browser: [e.g., Chrome 120]
26+
- Wallet: [e.g., Freighter 5.0]
27+
- Network: [e.g., Testnet, Futurenet]
28+
29+
## Screenshots
30+
If applicable, add screenshots.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
name: Feature Request
3+
about: Suggest a feature for SoroSave
4+
title: '[Feature] '
5+
labels: enhancement
6+
assignees: ''
7+
---
8+
9+
## Problem
10+
What problem does this feature solve?
11+
12+
## Proposed Solution
13+
How should this work?
14+
15+
## Alternatives Considered
16+
Other approaches you've thought about.
17+
18+
## Additional Context
19+
Any mockups, examples, or references.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
name: Good First Issue
3+
about: A beginner-friendly task
4+
title: ''
5+
labels: good first issue
6+
assignees: ''
7+
---
8+
9+
## Description
10+
What needs to be done.
11+
12+
## Context
13+
Why this is needed.
14+
15+
## Acceptance Criteria
16+
- [ ] Criterion 1
17+
- [ ] Criterion 2
18+
19+
## Hints
20+
Pointers to relevant files or docs.

.github/workflows/ci.yml

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
contract:
11+
name: Smart Contract
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: Install Rust
17+
uses: dtolnay/rust-toolchain@stable
18+
with:
19+
targets: wasm32-unknown-unknown
20+
21+
- name: Cache Cargo
22+
uses: actions/cache@v4
23+
with:
24+
path: |
25+
~/.cargo/registry
26+
~/.cargo/git
27+
target
28+
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
29+
30+
- name: Build contract
31+
run: cargo build --target wasm32-unknown-unknown --release
32+
33+
- name: Run tests
34+
run: cargo test
35+
36+
- name: Check formatting
37+
run: cargo fmt -- --check
38+
39+
- name: Clippy
40+
run: cargo clippy -- -D warnings
41+
42+
sdk:
43+
name: TypeScript SDK
44+
runs-on: ubuntu-latest
45+
steps:
46+
- uses: actions/checkout@v4
47+
48+
- name: Setup Node.js
49+
uses: actions/setup-node@v4
50+
with:
51+
node-version: '20'
52+
53+
- name: Install pnpm
54+
uses: pnpm/action-setup@v2
55+
with:
56+
version: 8
57+
58+
- name: Install dependencies
59+
run: pnpm install --filter @sorosave/sdk
60+
61+
- name: Build SDK
62+
run: pnpm --filter @sorosave/sdk build
63+
64+
frontend:
65+
name: Frontend
66+
runs-on: ubuntu-latest
67+
steps:
68+
- uses: actions/checkout@v4
69+
70+
- name: Setup Node.js
71+
uses: actions/setup-node@v4
72+
with:
73+
node-version: '20'
74+
75+
- name: Install pnpm
76+
uses: pnpm/action-setup@v2
77+
with:
78+
version: 8
79+
80+
- name: Install dependencies
81+
run: pnpm install
82+
83+
- name: Build frontend
84+
run: pnpm --filter @sorosave/frontend build
85+
env:
86+
NEXT_PUBLIC_CONTRACT_ID: "PLACEHOLDER"
87+
NEXT_PUBLIC_RPC_URL: "https://soroban-testnet.stellar.org"

.gitignore

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Rust
2+
/target/
3+
**/*.rs.bk
4+
Cargo.lock
5+
6+
# Node
7+
node_modules/
8+
.pnpm-store/
9+
dist/
10+
.next/
11+
out/
12+
13+
# Environment
14+
.env
15+
.env.local
16+
.env.*.local
17+
18+
# IDE
19+
.vscode/
20+
.idea/
21+
*.swp
22+
*.swo
23+
*~
24+
25+
# OS
26+
.DS_Store
27+
Thumbs.db
28+
29+
# Build artifacts
30+
*.wasm
31+
!contracts/**/*.wasm
32+
33+
# Test coverage
34+
coverage/
35+
.nyc_output/

ARCHITECTURE.md

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# SoroSave Architecture
2+
3+
## Overview
4+
5+
SoroSave is a decentralized rotating savings group (ROSCA) protocol. The system consists of three layers:
6+
7+
```
8+
┌─────────────────────────────────────────┐
9+
│ Frontend (Next.js) │
10+
│ Wallet connection, UI │
11+
├─────────────────────────────────────────┤
12+
│ SDK (TypeScript) │
13+
│ Transaction building, parsing │
14+
├─────────────────────────────────────────┤
15+
│ Smart Contract (Soroban) │
16+
│ Group management, contributions, │
17+
│ payouts, admin controls │
18+
├─────────────────────────────────────────┤
19+
│ Stellar Network │
20+
│ Consensus, token transfers │
21+
└─────────────────────────────────────────┘
22+
```
23+
24+
## Smart Contract Design
25+
26+
### Data Model
27+
28+
```
29+
┌──────────────────────────────────────────────────────┐
30+
│ SavingsGroup │
31+
│──────────────────────────────────────────────────────│
32+
│ id: u64 │
33+
│ name: String │
34+
│ admin: Address │
35+
│ token: Address (SAC token contract) │
36+
│ contribution_amount: i128 │
37+
│ cycle_length: u64 (seconds) │
38+
│ max_members: u32 │
39+
│ members: Vec<Address> │
40+
│ payout_order: Vec<Address> │
41+
│ current_round: u32 │
42+
│ total_rounds: u32 │
43+
│ status: GroupStatus │
44+
│ created_at: u64 │
45+
└──────────────────────────────────────────────────────┘
46+
47+
│ 1:N
48+
49+
┌──────────────────────────────────────────────────────┐
50+
│ RoundInfo │
51+
│──────────────────────────────────────────────────────│
52+
│ round_number: u32 │
53+
│ recipient: Address │
54+
│ contributions: Map<Address, bool> │
55+
│ total_contributed: i128 │
56+
│ is_complete: bool │
57+
│ deadline: u64 │
58+
└──────────────────────────────────────────────────────┘
59+
```
60+
61+
### State Machine
62+
63+
```
64+
┌──────────┐ start_group() ┌──────────┐
65+
│ Forming │─────────────────────▶│ Active │
66+
└──────────┘ └──────────┘
67+
│ │ │ │
68+
│ (members join/leave) │ │ │
69+
│ │ │ │
70+
│ pause() │ │ │ all rounds done
71+
│ ┌────────┘ │ └──────────────┐
72+
│ ▼ │ ▼
73+
│ ┌──────────┐ dispute() ┌──────────┐
74+
│ │ Paused │ │ │Completed │
75+
│ └──────────┘ │ └──────────┘
76+
│ │ ▼
77+
│ resume() │ ┌──────────┐
78+
│ └──────▶│ Disputed │
79+
│ └──────────┘
80+
│ │
81+
│ resolve() │
82+
│ ▼
83+
│ ┌──────────┐
84+
│ │ Active │
85+
└────────────────────────────┴──────────┘
86+
```
87+
88+
### Storage Layout
89+
90+
| Key | Storage Type | Description |
91+
|---|---|---|
92+
| `DataKey::Admin` | Instance | Protocol admin address |
93+
| `DataKey::GroupCounter` | Instance | Auto-incrementing group ID counter |
94+
| `DataKey::Group(id)` | Persistent | Group configuration and state |
95+
| `DataKey::Round(group_id, round)` | Persistent | Round contribution tracking |
96+
| `DataKey::MemberGroups(addr)` | Persistent | List of groups a member belongs to |
97+
| `DataKey::Dispute(group_id)` | Persistent | Active dispute information |
98+
99+
### Module Structure
100+
101+
```
102+
contracts/sorosave/src/
103+
├── lib.rs # Contract entry point, public API
104+
├── types.rs # Data structures (GroupStatus, SavingsGroup, etc.)
105+
├── errors.rs # ContractError enum
106+
├── storage.rs # Storage read/write helpers with TTL management
107+
├── group.rs # Group lifecycle (create, join, leave, start)
108+
├── contribution.rs # Contribution logic and token transfers
109+
├── payout.rs # Pot distribution and round advancement
110+
├── admin.rs # Governance (pause, dispute, emergency)
111+
└── test.rs # Unit tests
112+
```
113+
114+
### Token Integration
115+
116+
SoroSave uses Stellar Asset Contracts (SAC) for token transfers. The contract acts as an escrow:
117+
118+
1. **Contribute**: `token.transfer(member → contract, amount)`
119+
2. **Payout**: `token.transfer(contract → recipient, pot)`
120+
3. **Emergency**: `token.transfer(contract → each_member, balance / N)`
121+
122+
Members must authorize the contract to transfer tokens on their behalf via `require_auth()`.
123+
124+
## SDK Architecture
125+
126+
The SDK provides a typed TypeScript interface:
127+
128+
```
129+
SoroSaveClient
130+
├── createGroup() → Transaction
131+
├── joinGroup() → Transaction
132+
├── contribute() → Transaction
133+
├── distributePayout() → Transaction
134+
├── getGroup() → SavingsGroup (simulation)
135+
├── getRoundStatus() → RoundInfo (simulation)
136+
└── getMemberGroups() → number[] (simulation)
137+
```
138+
139+
Write operations return unsigned `Transaction` objects that must be signed (via Freighter or other wallet) before submission. Read operations use transaction simulation and return parsed data directly.
140+
141+
## Frontend Architecture
142+
143+
```
144+
Next.js App Router
145+
├── / (landing page)
146+
├── /groups (list all groups)
147+
├── /groups/new (create group form)
148+
└── /groups/[id] (group detail, contribute, manage)
149+
150+
Context Providers
151+
└── WalletContext (Freighter connection state)
152+
153+
Components
154+
├── Navbar, ConnectWallet
155+
├── GroupCard, GroupList
156+
├── CreateGroupForm
157+
├── MemberList, RoundProgress
158+
└── ContributeModal
159+
```
160+
161+
## Security Considerations
162+
163+
- **Authorization**: Every state-changing function requires `require_auth()` from the caller
164+
- **Admin separation**: Group admins manage their group; protocol admin handles emergencies
165+
- **Reentrancy**: Soroban's execution model prevents reentrancy by design
166+
- **Token safety**: All transfers use the standard Soroban token interface
167+
- **Dispute mechanism**: Any member can freeze a group; only admin can unfreeze
168+
- **TTL management**: Storage entries have TTL extensions to prevent data loss

0 commit comments

Comments
 (0)