Thank you for your interest in contributing to Scavenger — a Rust/Soroban smart contract system with a React/TypeScript frontend built on the Stellar blockchain. This document is the single authoritative reference for code style, the PR workflow, testing expectations, commit conventions, community standards, and environment setup.
- Getting Started / Setup
- Code Style Guidelines
- Pull Request Process
- Testing Requirements
- Commit Message Conventions
- Development Workflow
- Review Process
- Contributor Recognition
- Code of Conduct
- Rust toolchain — stable channel. Install via rustup:
rustup toolchain install stable rustup default stable rustup target add wasm32-unknown-unknown
- Soroban CLI — install the latest release:
cargo install --locked soroban-cli --features opt
- Node.js — LTS version (18+) recommended. Download from nodejs.org or use a version manager such as
nvm. - npm — bundled with Node.js; no separate install required.
# 1. Fork the repository on GitHub, then clone your fork
git clone https://github.com/<your-username>/Scavenger.git
cd Scavenger
# 2. Install Smart Contract dependencies
cargo build
# 3. Install Frontend dependencies
cd frontend
npm install
cd ..# Test Rust setup
cargo fmt --check
cargo clippy -- -D warnings
cargo test
# Test Frontend setup
cd frontend
npm run lint
npm run type-check
cd ..Before diving in, review the existing documentation:
README.md— project overview and architectureQUICKSTART.txt— fast-path setupPROJECT_SETUP.txt— detailed configurationdocs/— technical documentation
Consistent style keeps the codebase readable and review cycles short. Run all formatters and linters before pushing.
Run cargo fmt before every commit:
cargo fmtStyle rules:
- Max line length: 100 characters
- Use 4 spaces for indentation
- One blank line between functions
- Two blank lines between modules
Run cargo clippy and resolve all warnings:
cargo clippy -- -D warningsCommon issues to avoid:
- Unused imports
- Unnecessary clones
- Inefficient patterns
- Unwrap without justification
- Functions:
snake_case—register_participant,get_waste - Types/Structs:
PascalCase—ParticipantRole,WasteMetadata - Constants:
SCREAMING_SNAKE_CASE—MAX_WEIGHT,DEFAULT_TIMEOUT - Module names:
snake_case—participant_storage,waste_validation
- Keep functions focused on a single responsibility
- Avoid functions exceeding 50 lines; extract helpers when needed
- Use descriptive names that explain intent
- Document public functions with doc comments
Example:
/// Registers a new participant in the system.
///
/// # Arguments
/// * `address` - The participant's Stellar address
/// * `role` - The participant's role (Recycler, Collector, Manufacturer)
/// * `name` - Human-readable name
/// * `lat` - Latitude coordinate
/// * `lon` - Longitude coordinate
///
/// # Returns
/// Returns `Ok(())` on success or an error if validation fails.
pub fn register_participant(
env: &Env,
address: Address,
role: ParticipantRole,
name: String,
lat: i32,
lon: i32,
) -> Result<(), Error> {
// Implementation
}Prettier must pass with no diff:
npx prettier --check .
npx prettier --write . # Auto-fixStyle rules:
- Max line length: 100 characters
- Use 2 spaces for indentation
- Trailing commas in multi-line objects/arrays
- Single quotes for strings
ESLint must pass with no errors or warnings:
npx eslint .
npx eslint . --fix # Auto-fix- React components:
PascalCase—ParticipantForm,WasteCard - Types/Interfaces:
PascalCase—Participant,WasteData - Variables/functions:
camelCase—participantId,fetchWaste - Constants:
SCREAMING_SNAKE_CASE—MAX_RETRIES,API_TIMEOUT - File names:
kebab-casefor components —participant-form.tsx
- Keep components focused on a single concern
- Extract reusable logic into custom hooks
- Use TypeScript for type safety
- Avoid prop drilling; use context when appropriate
Example:
interface ParticipantFormProps {
onSubmit: (data: ParticipantData) => Promise<void>;
isLoading?: boolean;
}
export const ParticipantForm: React.FC<ParticipantFormProps> = ({
onSubmit,
isLoading = false,
}) => {
const [formData, setFormData] = useState<ParticipantData>({
name: '',
role: 'Recycler',
});
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
await onSubmit(formData);
};
return (
<form onSubmit={handleSubmit}>
{/* Form fields */}
</form>
);
};Fork the repository and create a dedicated branch:
git checkout -b feature/my-new-featureUse one of these prefixes with kebab-case description:
| Prefix | When to use | Example |
|---|---|---|
feature/ |
New functionality | feature/add-token-transfer |
fix/ |
Bug fixes | fix/null-pointer-crash |
docs/ |
Documentation | docs/update-setup-guide |
refactor/ |
Code restructuring | refactor/simplify-validation |
test/ |
Test additions | test/add-integration-tests |
Every PR must include:
## Description
Brief summary of changes.
## Motivation
Why this change is needed.
## Changes
- Bullet point 1
- Bullet point 2
## Testing
How to test these changes.
## Related Issues
Closes #123
## Checklist
- [ ] Code follows style guidelines
- [ ] Tests added/updated
- [ ] Documentation updated
- [ ] No breaking changesEnsure your PR targets main. Check branch protection rules before opening.
- Minimum one maintainer approval required
- All CI checks must pass
- Address all review comments before merge
Address one concern per PR. Split large changes into multiple PRs.
All Smart Contract changes must include tests:
# Run all tests
cargo test
# Run specific test
cargo test test_register_participant
# Run with output
cargo test -- --nocaptureTest structure:
#[test]
fn test_register_participant_success() {
let env = Env::default();
// Setup
// Execute
// Assert
}
#[test]
#[should_panic(expected = "error message")]
fn test_register_participant_invalid_role() {
// Test error case
}Frontend tests use Vitest:
# Run all tests
npm test
# Run specific test file
npm test participant-form
# Run with coverage
npm test -- --coverageTest structure:
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import { ParticipantForm } from './participant-form';
describe('ParticipantForm', () => {
it('should render form fields', () => {
render(<ParticipantForm onSubmit={vi.fn()} />);
expect(screen.getByLabelText(/name/i)).toBeInTheDocument();
});
it('should call onSubmit with form data', async () => {
const onSubmit = vi.fn();
render(<ParticipantForm onSubmit={onSubmit} />);
// User interactions
expect(onSubmit).toHaveBeenCalledWith(expectedData);
});
});- Minimum coverage: 80% for new code
- No reduction in overall coverage
- Critical paths must have 100% coverage
Create reusable test fixtures:
// tests/fixtures.rs
pub fn create_test_participant() -> Participant {
Participant {
address: Address::random(&env),
role: ParticipantRole::Recycler,
name: "Test User".to_string(),
lat: 0,
lon: 0,
}
}This project follows Conventional Commits v1.0.0.
<type>(<scope>): <short summary>
<body>
Closes #<issue-number>
| Type | When to use |
|---|---|
feat |
New feature |
fix |
Bug fix |
docs |
Documentation only |
chore |
Build, tooling, dependencies |
refactor |
Code restructuring |
test |
Adding/updating tests |
style |
Formatting, whitespace |
| Scope | Area |
|---|---|
contract |
Rust/Soroban smart contract |
frontend |
React/TypeScript frontend |
scripts |
Build scripts, CI, tooling |
docs |
Documentation files |
- Imperative mood: "add feature", not "added feature"
- Lowercase first letter
- 72 characters or fewer
- No period at end
- Separate from subject with blank line
- Explain why, not what
- Wrap at 72 characters
- Reference related issues
feat(contract): add token transfer function
Implements the transfer entrypoint as specified in the token interface.
Validates sender balance before executing the transfer.
Closes #17
fix(frontend): resolve null pointer on wallet disconnect
The wallet context was not guarded against undefined on disconnect,
causing a crash in the header component.
Closes #23
docs(contract): add participant registration examples
Added code examples showing how to register participants with different
roles and validate their information.
-
Create feature branch:
git checkout -b feature/my-feature
-
Make changes and test:
# For Rust changes cargo fmt cargo clippy -- -D warnings cargo test # For Frontend changes cd frontend npm run lint npm run type-check npm test
-
Commit with conventional messages:
git add . git commit -m "feat(contract): add new function"
-
Push and create PR:
git push origin feature/my-feature # Create PR on GitHub
Install pre-commit hooks to catch issues early:
# Install pre-commit framework
pip install pre-commit
# Install hooks
pre-commit install
# Run manually
pre-commit run --all-filesAll PRs run through automated checks:
- Rust checks: formatting, linting, tests, WASM build
- Frontend checks: formatting, linting, type checking, tests
- Security: dependency audit, secret scanning
All checks must pass before merge.
- Self-review before requesting review
- Respond to feedback promptly
- Request re-review after making changes
- Be respectful of reviewer time and expertise
- Review within 48 hours when possible
- Be constructive in feedback
- Approve when satisfied with changes
- Merge when approved and CI passes
- Code follows style guidelines
- Tests are adequate
- Documentation is updated
- No breaking changes
- Performance impact acceptable
- Security concerns addressed
We recognize and appreciate all contributions! Contributors are acknowledged in:
- Commit history — visible in
git log - GitHub contributors page — automatic
- Release notes — for significant contributions
- CONTRIBUTORS.md — maintained list (coming soon)
- Documentation: Typo fixes, guide improvements
- Bug fixes: Small fixes, edge cases
- Features: New functionality, enhancements
- Maintenance: Refactoring, optimization, tooling
All levels are valued and appreciated!
This project is committed to providing a welcoming and inclusive environment for everyone. We welcome contributors regardless of experience level, background, or identity.
We follow the Contributor Covenant as our Code of Conduct. All contributors and maintainers are expected to uphold these standards in all project spaces.
- Be respectful and considerate
- Welcome newcomers and different perspectives
- Accept constructive feedback gracefully
- Focus on what's best for the community
- Use inclusive language
- Harassment or discrimination
- Offensive comments or language
- Unwelcome sexual attention
- Trolling or insulting comments
- Publishing private information
If you experience or witness violations, please report to:
All reports are reviewed promptly and handled with discretion.
Violations may result in temporary or permanent exclusion from the project.
For the full text, visit Contributor Covenant.
- Stellar Documentation
- Soroban Documentation
- React Documentation
- TypeScript Handbook
- Conventional Commits
- Check existing GitHub Issues
- Review Discussions
- Ask in pull request comments
- Contact maintainers
Thank you for contributing to Scavenger! 🚀