|
| 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