Thanks for your interest in contributing to PropFi! We welcome contributions from everyone.
- Code of Conduct
- Getting Started
- Development Workflow
- Coding Standards
- Testing
- Pull Request Process
- Commit Conventions
- Contract Development Guide
By participating, you agree to maintain a respectful, inclusive, and harassment-free environment for everyone.
- Fork the repository.
- Clone your fork:
git clone https://github.com/your-username/propfi.git
- Set up the development environment as described in the README.
- Create a feature branch:
git checkout -b feat/my-feature
main ─── feat/my-feature ──→ PR ──→ merge to main
mainis the default branch. All PRs merge intomain.- Feature branches should be short-lived. Open a PR early (mark as draft) for feedback.
- Keep your branch up to date with
mainvia rebase, not merge.
- Formatting:
cargo fmt— all code must be formatted. - Linting:
cargo clippy -- -D warnings— no warnings allowed. - Naming:
- Contract functions:
snake_case. - Types and structs:
PascalCase. - Variables:
snake_case. - Constants:
SCREAMING_SNAKE_CASE.
- Contract functions:
- Errors: Use a
ContractErrorenum with#[contracterror]derive. - Events: Every state mutation must emit an event via
env.events().publish(). - Cross-contract calls: Always use the
Clientpattern generated by#[contractclient]. - Admin gating: Use
require_auth()andAddress::require_auth()for access control. - No unsafe code:
unsafeis not permitted unless absolutely necessary and reviewed. - No panics: Use
Resultreturn types instead of.unwrap()or.expect()in production code (tests are exempt).
- Formatting: Use
prettierwith the project config. - Linting: ESLint with the provided config.
- Naming:
- Functions and variables:
camelCase. - Types and interfaces:
PascalCase. - Constants:
UPPER_SNAKE_CASE. - Files:
kebab-case.ts.
- Functions and variables:
- Types: Prefer
interfaceovertypefor object shapes. Usetypefor unions and aliases. - Async: Use
async/awaitconsistently. Avoid raw.then(). - Imports: Group imports: external → internal. Use named exports unless a default export is required by a framework.
- No commented-out code.
- No debug logging or
console.login production code. - Document public APIs with doc comments (
///in Rust,/** */in TypeScript). - Keep functions small and single-purpose.
All changes must include tests.
- Place tests in a
#[cfg(test)] mod testsblock at the bottom of the source file. - Use Soroban's test harness (
SorobanTestorEnv::default()). - Test both happy paths and error cases.
- Run tests before pushing:
cargo test --workspace
- Tests live in
__tests__/directories alongside the source. - Use
vitestorjestas configured. - Run tests before pushing:
cd sdk && npm test cd indexer && npm test
- Integration tests live in
contracts/integration_tests/. - Ensure integration tests pass for any cross-contract changes:
cargo test -p propfi-integration-tests
- Before opening a PR, ensure:
cargo test --workspacepasses.cargo clippy -- -D warningsis clean.cargo fmt --checkis clean.- TypeScript projects build (
npm run build).
- PR title should follow Conventional Commits.
- PR description must include:
- What this PR does.
- Why it's needed (link to issue if applicable).
- How it was tested.
- PR size: Keep PRs focused. Split large changes into multiple PRs.
- Review: At least one maintainer approval is required before merging.
- Merge: Squash-merge into
mainwith a clean commit message.
Use Conventional Commits:
<type>(<scope>): <description>
[optional body]
Types: feat, fix, refactor, test, docs, style, chore, ci.
Examples:
feat(property-registry): add valuation update with oracle verification
fix(fraction-vault): prevent overflow in buy_fraction calculation
test(mortgage-pool): add liquidation edge case tests
docs(README): update deployment instructions
- Create
contracts/<name>/Cargo.toml— use an existing contract'sCargo.tomlas a template. - Create
contracts/<name>/src/lib.rswith a module structure. - Add the contract to the workspace
Cargo.toml. - Implement
initialize, core functions, events, and tests. - Add integration test coverage in
tests/integration/.
When your contract calls another PropFi contract:
- Import the client:
use crate::property_registry::ContractClient as PropertyRegistryClient; - Pass the contract address as a function parameter.
- Gate sensitive operations with
ComplianceRegistryClient::is_compliant(). - Add the dependency to
Cargo.toml(path dependency).
Events should follow the pattern VerbPastTense:
PropertyRegistered,FractionPurchased,YieldDistributed,LoanOpened.
Each event payload must contain the affected address or ID as the first key for easy indexing.