Thank you for your interest in contributing to Stellar-Save — a decentralized ROSCA (Rotating Savings and Credit Association) built on Stellar Soroban smart contracts.
This guide covers everything you need to get started: environment setup, coding standards, testing requirements, and the PR process.
New to the project? Start with docs/first-time-contributor.md for a gentler walkthrough.
- Code of Conduct
- Architecture Overview
- Development Setup
- Project Structure
- Coding Standards
- Commit Message Conventions
- Testing Requirements
- Pull Request Process
- Drips Wave Contributions
- Getting Help
By participating in this project you agree to abide by our Code of Conduct. We do not tolerate harassment, discrimination, or hostile behaviour. Report violations by opening a private issue or contacting a maintainer directly.
Stellar-Save has four main layers:
User (Stellar wallet)
│
▼
Frontend (React + TypeScript + Vite)
│
▼
Soroban Smart Contracts (Rust)
│
▼
Stellar Network (on-chain storage + Horizon API)
Smart contract modules (contracts/stellar-save/src/):
| Module | Responsibility |
|---|---|
lib.rs |
Contract entry points and public API |
group.rs |
Group creation and configuration |
contribution.rs |
Contribution logic and tracking |
payout.rs / payout_executor.rs |
Payout rotation and distribution |
storage.rs |
On-chain data layout |
security.rs |
Authorization and access control |
error.rs |
Typed error variants |
events.rs |
Soroban event emission |
Frontend (frontend/src/): React 19 + TypeScript SPA using MUI, React Router, and @stellar/stellar-sdk.
For full architecture details see docs/architecture.md.
| Tool | Version | Install |
|---|---|---|
| Rust | 1.81.0 (pinned) | rustup.rs |
| Soroban / Stellar CLI | latest | Stellar CLI docs |
| Node.js | 18+ | nodejs.org |
| npm | 9+ | bundled with Node.js |
The Rust toolchain version is pinned in rust-toolchain.toml. Running any cargo command will install it automatically via rustup.
git clone https://github.com/Xoulomon/Stellar-Save.git
cd Stellar-Save
# Install root-level tooling (commitlint, husky)
npm install
# Install frontend dependencies
cd frontend && npm install && cd ..cp .env.example .envEdit .env with your network settings. Available networks are defined in environments.toml:
testnet— Stellar testnet (recommended for development)futurenet— Stellar futurenetstandalone— Local development nodemainnet— Production (do not use for development)
./scripts/build.sh
# or directly:
cargo build --target wasm32-unknown-unknown --releasecd frontend
npm run dev# Generate a testnet identity (one-time)
stellar keys generate deployer --network testnet
# Deploy
./scripts/deploy_testnet.shStellar-Save/
├── contracts/
│ └── stellar-save/ # Main ROSCA smart contract (Rust)
│ └── src/ # Contract modules
├── frontend/ # React + TypeScript SPA
│ └── src/
├── client/ # Rust client library
├── scripts/ # Build, deploy, and test scripts
├── docs/ # Project documentation
├── tests/ # Integration and shell tests
├── infra/ # Terraform infrastructure
├── monitoring/ # Prometheus / Grafana / ELK configs
├── .github/workflows/ # CI/CD pipelines
├── Cargo.toml # Workspace manifest
├── environments.toml # Network configurations
└── rust-toolchain.toml # Pinned Rust version
- Run
cargo fmtbefore every commit — formatting is enforced in CI - Run
cargo clippy -- -D warningsand fix all warnings before opening a PR - Keep functions small and single-purpose
- Use descriptive names; avoid single-letter variables outside iterators
- Document all public items with
///doc comments - Prefer
Result<T, ContractError>over panics for recoverable errors - Use the typed error variants in
error.rs— do not add barepanic!calls
/// Verifies the caller is the group creator.
///
/// # Errors
/// Returns [`ContractError::Unauthorized`] if the caller is not the creator.
pub fn require_creator(env: &Env, group: &Group) -> Result<(), ContractError> {
let caller = env.invoker();
if caller != group.creator {
return Err(ContractError::Unauthorized);
}
Ok(())
}- Use functional components with hooks — no class components
- Type all props and state with TypeScript interfaces or types; avoid
any - Use
constby default;letonly when reassignment is necessary - Keep components under ~150 lines; extract sub-components when they grow larger
- Use semantic HTML for accessibility (
<button>,<nav>,<main>, etc.) - Run
npm run lintbefore committing — ESLint is enforced in CI
Prettier config (.prettierrc):
- Single quotes, semicolons, trailing commas (ES5), 100-char print width, 2-space indent
interface ContributionCardProps {
amount: bigint;
member: string;
isPaid: boolean;
}
const ContributionCard = ({ amount, member, isPaid }: ContributionCardProps) => (
<article className="contribution-card">
<span>{member}</span>
<span>{isPaid ? '✓' : 'Pending'}</span>
</article>
);.editorconfigis present — use an editor that respects it (UTF-8, LF line endings, final newline)- Do not commit secrets, private keys, or
.envfiles —.gitignorecovers common cases but double-check before staging
We use Conventional Commits. Commits are validated by commitlint via a Husky commit-msg hook.
<type>(<scope>): <short description>
[optional body]
[optional footer(s)]
| Type | Use for |
|---|---|
feat |
New feature |
fix |
Bug fix |
docs |
Documentation only |
style |
Formatting, whitespace (no logic change) |
refactor |
Code restructuring without behaviour change |
perf |
Performance improvement |
test |
Adding or updating tests |
chore |
Build process, dependency updates, tooling |
ci |
CI/CD configuration changes |
revert |
Reverting a previous commit |
- Use imperative mood: "add" not "added" or "adds"
- Keep the subject line under 100 characters
- Reference issues in the footer:
Closes #42 - Use
feat!or addBREAKING CHANGE:in the footer for breaking changes
feat(contract): add penalty mechanism for missed contributions
fix(frontend): correct off-by-one in payout position display
docs: expand contributing guide with architecture overview
test(contract): add fuzz tests for contribution overflow edge cases
chore: update soroban-sdk to 23.0.3
All new public functions must have tests covering:
- The happy path
- Expected error cases (use
assert_eq!(result, Err(ContractError::...)))
Run the full test suite before opening a PR:
# All contracts
cargo test --workspace
# Stellar-save contract only
cargo test -p stellar-save
# With stdout output
cargo test -- --nocapture
# With coverage (requires cargo-tarpaulin)
cargo tarpaulin --config contracts/stellar-save/tarpaulin.tomlTest snapshots live in contracts/stellar-save/test_snapshots/. Update them if your change intentionally affects output.
Add tests for new utility functions and hooks. Component tests are encouraged.
cd frontend
# Watch mode (development)
npm test
# Single run (CI)
npm test run
# With coverage
npm run test:coverage
# Accessibility checks are included via jest-axe / vitest-axeTest files live alongside source files as *.test.ts / *.test.tsx. Setup is in src/test/setup.ts.
- Do not reduce overall test coverage — PRs that delete tests without replacement will be rejected
- If you find a bug, write a failing test that reproduces it before fixing it
- CI must be green before requesting review
-
Open an issue first for non-trivial changes — discuss the approach before investing time coding
-
Branch from
main— never commit directly tomaingit checkout main && git pull origin main git checkout -b feat/your-feature-nameBranch naming conventions:
feat/description— new featurefix/description— bug fixdocs/description— documentationrefactor/description— refactoringtest/description— tests only
-
Keep PRs focused — one feature or fix per PR; avoid bundling unrelated changes
-
Fill in the PR template completely — describe what changed, why, and how to test it
-
Ensure CI passes — all checks must be green before requesting review
-
Request a review from at least one maintainer
-
Address review comments — push follow-up commits to the same branch; do not force-push after review has started
-
Squash on merge — maintainers squash commits when merging to keep history clean
Follow the same Conventional Commits format as commit messages:
feat(contract): implement penalty for missed contributions
fix(frontend): resolve wallet connection timeout on mobile
Stellar-Save participates in Drips Wave — a contributor funding program. Funded issues are labelled wave-ready on GitHub and categorised by effort:
| Label | Points | Examples |
|---|---|---|
trivial |
100 | Documentation fixes, simple tests, minor UI copy |
medium |
150 | Helper functions, validation logic, moderate features |
high |
200 | Core features, complex integrations, security enhancements |
See docs/wave-guide.md for how to claim and earn funding.
- GitHub Issues — bug reports and feature requests
- GitHub Discussions — questions, ideas, and general conversation
- Telegram — @Xoulomon for quick questions
If you are unsure whether your idea fits the project, open a Discussion before writing code. We are happy to help you get your contribution across the line.